一直以來都有把.py文件打包成.exe文件的想法,但老是不夠強烈,每次拖着拖着就淡忘了。html
昨天幫硬件部門的同事寫了個腳本,而後今天下午的時候,他問有沒有辦法把腳本打包成可執行文件,這樣方便之後交給別人的時候別人不用裝Python也能運行。python
習慣性操做,百度一下,看到標題都基本使用PyInstaller,而後直接進官方文檔。app
文件其實無所謂,隨便使用一個正確的Python腳本文件便可。我這裏直接使用給同事寫好的文件tracer.py(其實主要是爲了對這個腳本作個記錄方便之後本身找,若是使用我這個須要安裝openpyxl庫)。ide
import logging import os import openpyxl class MatterTracer(): def __init__(self): self.config_dict = { # ERP導出文件存放目錄 "erp_xls_dir":"./ERP/", # ERP導出文件的表名 "erp_xls_sheet_name": "sheet1", # ERP導出文件存貨編號列 "erp_part_number_column":"B", # ERP導出文件規格型號列 "erp_description_column":"D", # ERP導出文件物料狀態列 "erp_approved_column":"E", # ERP導出文件物料實際起始行 "erp_start_rank_no":2, # 本地維護文件存放目錄 "local_xls_dir":"./LOCAL/", # 最終文件輸出目錄 "local_xls_save_dir":"./LOCAL_NEW/", # 本地維護文件的表名 "local_xls_sheet_name": "Sheet1", # 本地維護文件存貨編號列 "local_part_number_column":"A", # 本地維護文件規格型號列 "local_description_column": "D", # 本地維護文件物料狀態列 "local_approved_column": "H", # 本地維護文件物料實際起始行 "local_start_rank_no":2, } # ERP導出文件清單 self.erp_xls_list = ["IC.XLSX","插件.XLSX","貼片.XLSX"] # self.erp_xls_list = ["IC.XLSX",] # 本地維護文件清單 self.local_xls_list = ["capacitor.xlsx","connector.xlsx","diode.xlsx","ic.xlsx","magnetic.xlsx","miscellaneous.xlsx","mosfet.xlsx","nonum.xlsx","protect.xlsx","resistor.xlsx"] # self.local_xls_list = ["capacitor.xlsx",] def tracer_follow(self): erp_xls_wbs = [] local_xls_wbs = [] # 反覆打開文件是比較耗性能的,因此先統一打開ERP導出的文件 for erp_xls_name in self.erp_xls_list: logging.warning(f"start to open {erp_xls_name}") erp_xls_wbs.append(openpyxl.load_workbook(f"""{self.config_dict["erp_xls_dir"]}{erp_xls_name}""")) # 反覆打開文件是比較耗性能的,因此先統一打開本地維護的文件 for local_xls_name in self.local_xls_list: logging.warning(f"start to open {local_xls_name}") local_xls_wbs.append(openpyxl.load_workbook(f"""{self.config_dict["local_xls_dir"]}{local_xls_name}""")) # 遍歷全部從ERP導出的文件 for erp_xls_wb in erp_xls_wbs: erp_xls_sheet = erp_xls_wb[self.config_dict["erp_xls_sheet_name"]] erp_rank_no = self.config_dict["erp_start_rank_no"] # 遍歷ERP導出文件的全部行,以物料號爲空做爲結束標誌 while erp_xls_sheet[f"""{self.config_dict["erp_part_number_column"]}{erp_rank_no}"""].value is not None: # 遍歷全部本地維護的文件 # 是否找到匹配行的標誌 logging.warning(f"""{self.config_dict["erp_part_number_column"]}{erp_rank_no}: start to find match""") find_flag = False for local_xls_wb in local_xls_wbs: local_xls_sheet = local_xls_wb[self.config_dict["local_xls_sheet_name"]] local_rank_no = self.config_dict["local_start_rank_no"] # 遍歷本地文件的全部行,以物料號爲空做爲結束標誌 while local_xls_sheet[f"""{self.config_dict["local_part_number_column"]}{local_rank_no}"""].value is not None: # 若是ERP物料號與本地物料號相等 if erp_xls_sheet[f"""{self.config_dict["erp_part_number_column"]}{erp_rank_no}"""].value == local_xls_sheet[f"""{self.config_dict["local_part_number_column"]}{local_rank_no}"""].value: local_xls_sheet[f"""{self.config_dict["local_description_column"]}{local_rank_no}"""].value = erp_xls_sheet[f"""{self.config_dict["erp_description_column"]}{erp_rank_no}"""].value local_xls_sheet[f"""{self.config_dict["local_approved_column"]}{local_rank_no}"""].value = erp_xls_sheet[f"""{self.config_dict["erp_approved_column"]}{erp_rank_no}"""].value find_flag = True # 若是找到匹配行,則後續記錄不用再繼續找了 logging.warning(f"""{self.config_dict["erp_part_number_column"]}{erp_rank_no}({erp_xls_sheet[f'{self.config_dict["erp_part_number_column"]}{erp_rank_no}'].value}): found match""") break # 本地文件切換至下一行 # logging.warning(f"""{self.config_dict["erp_part_number_column"]}: not match with """) local_rank_no += 1 # 若是找到匹配行,則後續文件不用再繼續找了 if find_flag: break # 若是退出文件遍歷仍是沒找到匹配項 if not find_flag: logging.warning(f"""{self.config_dict["erp_part_number_column"]}{erp_rank_no}({erp_xls_sheet[f'{self.config_dict["erp_part_number_column"]}{erp_rank_no}'].value}): have no match""") # ERP文件切換至下一行 erp_rank_no += 1 # 若是保存的目錄不是打開的目錄,則先判斷要保存的目錄是否存在,不存在則先建立 if self.config_dict["local_xls_save_dir"] != self.config_dict["local_xls_dir"]: logging.warning(f"""local_xls_save_dir {self.config_dict["local_xls_save_dir"]} is not local_xls_dir {self.config_dict["local_xls_dir"]}""") if not os.path.exists(self.config_dict["local_xls_save_dir"]): os.mkdir(self.config_dict["local_xls_save_dir"]) # 遍歷本地維護的全部文件 flag = 0 for local_xls_wb in local_xls_wbs: local_xls_wb.save(f"""{self.config_dict["local_xls_save_dir"]}{self.local_xls_list[flag]}""") flag += 1 pass if __name__ == "__main__": mt = MatterTracer() mt.tracer_follow()
直接pip安裝便可,其餘依賴pip會自動安裝上不用管。函數
pip install pyinstaller
pyinstaller.exe位置:和習慣同樣,安裝PyInstaller庫後,其對應的可執行文件(若是有)也後被放到Python環境的Scripts目錄下。若是要使用要麼使用全路徑,要麼把Scripts目錄加入PATH環境變量,我這裏已加入環境變量。性能
打包時直接pyinstaller帶main函數入口所在python文件便可,如我這裏就是(打包過程當中360可能會告警注意放行一下便可):ui
pyinstaller tracer.py
# 默認文件放到一個目錄下,但其實可經過如下形式,將全部文件都集合成一個exe文件
# 但實際操做又發現放到一個文件,logging等的打印都不在控制檯輸出,因此也並不是那麼好用
# pyinstaller --onefile --windowed tracer.py
執行以上命令後,pyinstaller默認會在當前目錄下:spa
建立與腳本文件同名的.spec文件(如我這裏就是tracer.spec),該文件是pyinstaller分析腳本文件生成的打包指導文件,後邊會根據該文件進行打包。操作系統
建立build目錄(若是該目錄不存在),該目錄用於打包過程生成的臨時文件,最終咱們並不須要該目錄。插件
建立dist目錄(若是該目錄不存在),該目錄存放的就是最終打包出的結果,其下包括可執行文件及其依賴庫。
另外注意雙擊運行exe文件,和直接運行腳本文件是同樣的,即代碼須要讀寫的目錄/文件也須要放到當前目錄下來,否則運行報錯。
上邊咱們說打包實際上是根據.spec文件的指導進行的,若是咱們想作一些定製打包操做(好比指定生成的exe文件名),能夠直接修改.spec文件,而後使用相似以下形式打包:
pyinstaller tracer.spec
打包原理:PyInstaller讀取給定的Python腳本進行遞規分析,找出全部Python腳本執行所需的模塊和DLL庫,而後複製一份統一放到dist目錄下。
關於跨平臺:不一樣操做系統可執行文件的格式是不同的、動態庫也是不同的,因此PyInstaller雖然支持Windows/Linux/Mac但顯然不能一處打包處處運行,只能要生成哪一個平臺的可執行文件就要在哪一個平臺打包。
參考: