PyInstaller把Python腳本打包成可執行程序教程


一、說明

一直以來都有把.py文件打包成.exe文件的想法,但總是不夠強烈,每次拖着拖着就淡忘了。

昨天幫硬件部門的同事寫了個腳本,然后今天下午的時候,他問有沒有辦法把腳本打包成可執行文件,這樣方便以后交給別人的時候別人不用裝Python也能運行。

習慣性操作,百度一下,看到標題都基本使用PyInstaller,然后直接進官方文檔。

 

二、打包操作

2.1 待打包文件

文件其實無所謂,隨便使用一個正確的Python腳本文件即可。我這里直接使用給同事寫好的文件tracer.py(其實主要是為了對這個腳本做個記錄方便以后自己找,如果使用我這個需要安裝openpyxl庫)。

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()
View Code

 

2.2 安裝PyInstaller

直接pip安裝即可,其他依賴pip會自動安裝上不用管。

pip install pyinstaller

 

2.3 基本打包操作

pyinstaller.exe位置:和習慣一樣,安裝PyInstaller庫后,其對應的可執行文件(如果有)也后被放到Python環境的Scripts目錄下。如果要使用要么使用全路徑,要么把Scripts目錄加入PATH環境變量,我這里已加入環境變量。

打包時直接pyinstaller帶main函數入口所在python文件即可,如我這里就是(打包過程中360可能會告警注意放行一下即可):

pyinstaller tracer.py
# 默認文件放到一個目錄下,但其實可通過以下形式,將所有文件都集合成一個exe文件
# 但實際操作又發現放到一個文件,logging等的打印都不在控制台輸出,所以也並非那么好用
# pyinstaller --onefile --windowed tracer.py

執行以上命令后,pyinstaller默認會在當前目錄下:

創建與腳本文件同名的.spec文件(如我這里就是tracer.spec),該文件是pyinstaller分析腳本文件生成的打包指導文件,后邊會根據該文件進行打包。

創建build目錄(如果該目錄不存在),該目錄用於打包過程生成的臨時文件,最終我們並不需要該目錄。

創建dist目錄(如果該目錄不存在),該目錄存放的就是最終打包出的結果,其下包括可執行文件及其依賴庫。

另外注意雙擊運行exe文件,和直接運行腳本文件是一樣的,即代碼需要讀寫的目錄/文件也需要放到當前目錄下來,不然運行報錯。

 

2.4 定制化打包操作【可選】

上邊我們說打包其實是根據.spec文件的指導進行的,如果我們想做一些定制打包操作(比如指定生成的exe文件名),可以直接修改.spec文件,然后使用類似如下形式打包:

pyinstaller tracer.spec

 

三、一些說明

打包原理:PyInstaller讀取給定的Python腳本進行遞規分析,找出所有Python腳本執行所需的模塊和DLL庫,然后復制一份統一放到dist目錄下。

關於跨平台:不同操作系統可執行文件的格式是不一樣的、動態庫也是不一樣的,所以PyInstaller雖然支持Windows/Linux/Mac但顯然不能一處打包到處運行,只能要生成哪個平台的可執行文件就要在哪個平台打包。

 

參考:

https://pypi.org/project/PyInstaller/

https://pyinstaller.readthedocs.io/en/v3.5/usage.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM