• 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

  • 相关阅读:
    from __future__ import with_statement
    第六章 Flask数据库(二)
    python面向对象基础(四)内置方法 __xx__之new与init
    MySQL用户自定义变量
    iOS不得姐项目--appearance的妙用,再一次设置导航栏返回按钮,导航栏左右按钮的封装(巧用分类)
    iOS边练边学--NSURLSessionDataTask实现文件真正的断点续传
    iOS边练边学--AFNetWorking框架GET、Post、Download、Upload,数据解析模式以及监控联网状态
    iOS边练边学--NSURLSession、NSURLSessionTask的介绍与使用以及url中包含了中文的处理方法
    iOS开发小技巧--边接受数据边写入文件的两种方法
    iOS边练边学--文件压缩和解压缩的第三方框架SSZipArchive的简单使用
  • 原文地址:https://www.cnblogs.com/lsdb/p/11425388.html
Copyright © 2020-2023  润新知