Python | 打包文件 | 打包 py 文件为可执行文件 .exe

本文总结如何将 python 文件打包成可执行文件 .exe

Updated: 2022 / 10 / 7


Python | 打包文件 | 打包 py 文件为可执行文件 .exe

  • 总览
    • Pyinstaller
    • other
  • 使用
    • 安装
    • 打包
      • 用法
        • pyinstaller命令
        • spec文件
          • 修改spec文件
        • pipenv 虚拟环境
      • 应用
        • 简单的 .py 文件
        • 存在前提依赖的复杂项目
        • 问题、处理及注意事项
  • 示例
    • 依赖资源文件
      • Unix / Linux / MacOS
        • 仅打包 py 文件
        • 打包依赖资源文件
          • 先打包后修改spec
          • 先配置spec后打包
      • windows
        • 仅打包 py 文件
  • 参考链接


总览

py 文件打包成 exe 文件的方式一共有三种:

  • py2exe
  • PyInstaller
  • cx_Freeze

python 作为一门解释型脚本语言,它有三种发布方式 1

  • py 文件
    源码文件,运行需要使用者安装 Python 环境并且安装依赖的各种库;
  • pyc 文件
    pyc 文件是 Python 解释器可以识别的二进制码,可跨平台的,需要使用者安装相应版本的Python 和依赖库。
  • 可执行文件
    不需要安装 python 环境和依赖库,可针对不同平台需要打包不同的可执行文件( Windows , Linux, Mac, …)

Pyinstaller

PyInstaller freezes (packages) Python applications into stand-alone executables, under Windows, GNU/Linux, Mac OS X, FreeBSD, Solaris and AIX.

当通过 Python 开发的程序应用需要在其他设备上运行时,需要安装 Python 环境及相应的第三方库,但对方是非技术人员或在离线环境下时,搭建环境比较麻烦,此时可考虑使用 PyInstaller 工具将 python 解析器和脚本打包成一个可执行的文件。
无论是生成的文件夹里的可执行文件或者只打包成一个可执行文件都可以直接运行,前者需要把整个文件夹都给别人。可能运行效率可能会降低,好处就是在使用者的机器上可以不用安装 python 和脚本所依赖的库。

  • 打包成一个文件夹 2

传输方便(压缩,解压到另外一个电脑,再双击 exe 文件就可以运行),而且debug方便。使用这一方式时,很容易调试在打包 exe 时出现问题。可以准确地看到 pyinstaller 收集了哪些文件到该文件夹。
如果代码发生改变(不涉及依赖包的改变的话),只需要发给别人更新过的 .exe 文件即可。这比更新整个文件夹要方便地多。

但是,如果脚本引入了新的依赖包或更改了依赖包,则需要重新分发这一整个文件夹。

  • 打包成一个单独的文件 2

pyinstaller 可以把所有的脚本及其依赖包都打包到一个 .exe 文件中,优点是看不懂文件夹中其他内容的用户可以只得到一个是做什么的 .exe 文件。

但是每次更新都要重新发布全部内容(只有1个 .exe 但是体积会大一些),同时,单个文件比单个文件夹启动要慢。

利用 PyInstaller 对指定的的脚本打包时,会先分析脚本所依赖的其他脚本,然后根据导包路径去查找,把所有相关的脚本收集起来,包括 Python 解析器,然后根据你的命令参数可分别生成文件夹,或者打包成一个可执行文件。

注意
PyInstaller 本身可跨平台使用,但通过 PyInstaller 打包出来的文件是无法跨平台的,比如在 Linux 下使用 PyInstaller 打包 Python 程序得到的文件只可在 Linux 下运行;在 Windows 下打包的只可在 Windows 下运行。使用 Pyinstaller 打包得到的 .exe 文件是特定于具体的操作系统和特定的 python 版本的。
也就是说,不具备可移植性。

若要为以下的环境准备发布版本:

  • 不同的操作系统
  • 不同的Python版本
  • 32/64位不同

应该针对该平台进行打包,即可以在那个 python 版本,那个操作系统下运行 Pyinstaller。然后执行 PyinstallerPython 编译器是 bundle(包)的一部分,它会特定于具体的操作系统和特定的字长。


other

参考这里 3

马克


使用

PyInstaller analyzes yourprogram.py and:

Writes yourprogram.spec in the same folder as the script
Creates a folder build in the same folder as the script if it does not exist
Writes some log files and working files in the build folder
Creates a folder dist in the same folder as the script if it does not exist
Writes the yourprogram executable folder in the dist folder
In the dist folder you find the bundled app you distribute to your users

安装

  • 联网:
    [x] 安装
    pip install pyinstaller,或者,
    python -m pip install pyinstaller -i http://mirrors.aliyun.com/pypi/simple/,使用 -i 指定镜像即可
    [x] 更新
    pip install --upgrade pyinstaller

  • 离线:
    先去官网下载离线安装源 (https://pypi.org/project/PyInstaller/#files),再进行安装。

注意 4
win 安装 PyInstaller 时需要额外安装另外两个模块,pywin32pypiwin32,以及 pefile
如果使用 pip 进行安装,但是还没有安装 pywin32 时,会自动安装 pypiwin32pefile 没有时也会自动安装。


打包

用法

基本的命令为 pyinstaller -option xxx.py

其中 xxx.py 就是需要打包的 python 脚本,optionpyinstaller 中的参数选项,其中 option 默认为 -D

打包完成后主要生成两个文件夹和一个 spec 文件:

  1. dist 文件夹,运行 pyinstaller 命令后会在相同路径下生成,该文件夹下应该会有一个跟程序同名的文件夹(除非使用 pyinstaller -F 命令打包),打包好的 exe 就在该文件夹下。

如果在命令行中或者 .spec 文件中指定的 .py 文件不止一个,比如 pyinstaller xxx1.py xxx2.pypyinstaller 会依次分析并执行,并把第一个 py 名称作为 .specdist 文件下的文件夹和程序的名称。

  1. build 文件夹,运行 pyinstaller 命令后会在相同路径下生成,相当于 Pyinstaller 的工作空间,Pyinstaller 运行相关的文件和日志都在这个文件夹中,打包完成后可以直接删除;
  2. .spec 文件,即配置文件,用于打包复杂的 python 项目。

pyinstaller命令

  1. options 参数说明
参数名 描述 说明
-h / --help 显示帮助信息
-v / --version 显示版本号
--distpath 生成文件放在哪里 默认:当前目录的 dist 文件夹内

  1. 与生成结果有关参数
参数名 描述 说明
-D 生成 one-folder 的程序(默认),比如 pyinstaller -D demo.py 生成结果是一个目录,各种第三方依赖文件和 DLL 文件等和 demo.exe 同时存储在该目录下。

打包的文件始终在 Pyinstallerbootloader(根引导)中开始执行,这是打包的文件夹中可执行文件的核心。 Pyinstaller bootloader 是活动平台(windows GNU / linux / Mac OS 等)上的一个二进制可执行程序,当用户启动程序时,其实是 bootloader 在运行。bootloader 创建了一个临时的 Python 虚拟环境,这样 Python 编译器 (interpreter) 就可以在这个脚本文件夹中找到所有引入的模块 / 库。bootloader 启动了一个 Python 编译器的副本来执行脚本,后续的执行正常都是从这里开始的,提供被包括在脚本文件夹中所有支持的文件 2
-F 生成 one-file 的程序,比如 pyinstaller -F demo.py 只在 dist 文件夹中生成一个结果是 demo.exe 文件,不生成其他 dll 文件,适用于没有多依赖 .py 文件的单个文件。可以覆盖打包,无论打包多少次都将是最新的。

-D 模式生成的 exe 程序相比,在启动速度上会慢一些,因为它需要先解压 exe 文件并生成一个唯一的临时环境来运行程序,关闭环境时也会自动删除这个临时环境。而 -D 模式的程序本身就是解压好的,运行完也不需要执行删除操作。

这一点,在程序的大小比较大时,这个差别就很明显了 2

运行程序的时候,也会有一个 BootLoader,但是会根据操作系统创建一个名为 _MElxxxxxx 的文件夹,用作这个程序的临时运行环境(不只是 python 环境),这个 xxxxxx 是一个随机的数字。-F 模式程序启动时因为需要解压并拷贝依赖和资源文件到临时运行环境 _MExxxxxx,所以启动速度比 -D 模式慢,运行结束后会删除临时运行环境的文件夹。在 Linux 和相关系统中,可能有 no-execution 选项,但是对于 -F 模式程序是不兼容的。由于 _MElxxxxxx 是唯一的,所以可以同时运行多个程序,多个程序互不干涉。如果程序崩溃了,或者强行结束(在 win 的任务管理器中杀死了进程),_MElxxxxxx 文件夹是不会被删除的,所以频繁崩溃或结束进程会导致有多个 _MElxxxxxx 文件夹,会非常占磁盘空间,可以使用 --runtime-temdir 指定 _MElxxxxxx 的存放位置。-F 模式程序如果在运行时遇到了权限问题,可以使用 -D 模式程序替代。
-K -tk,在部署时包含 TCL / TK
-n NAME --name NAME,生成 .exe 文件和 .spec 的文件名 默认为传入的 .py 脚本或者 .spec 文件的名称。
--specpath DIR 指定生成 spec 文件的路径,默认为当前路径。 比如,pyinstaller --specpath ./SPEC demo1.py demo2.py
--distpath DIR 指定生成 dist 的目录,默认为 ./dist
--workpath WORKPATH 指定 pyinstaller 的工作目录,即 build 文件夹,默认为 ./build
-y / --noconfirm 替换输出目录时不询问,默认输出目录是 SPECPATH / dist / SPECNAME
--upx-dir UPX_DIR 指定 UPX 程序的路径,默认为 程序执行路径, 即双击某个文件时,系统自动查找对应程序的路径。UPX 为一个压缩程序,需要自行下载,可以将 exe 压缩为 zip 格式的文件,并且压缩效率非常高。如果打包后的 exe 程序非常大的话,为了避免客户下载时文件太大的问题,可以使用 UPX 工具。
-d -debug,产生 debug 版本的可执行文件。比如 pyinstaller demo.py -d
--noupx 不需要 UPX (即便可用)
-a / --ascii 不包含编码。在支持 Unicodepython 版本上默认包含所有的编码。
--clean pyinstaller 开始执行前清楚缓存并删除临时文件(一般存储在 C:\Users\Administrator\AppData\Roaming\pyinstaller)。
--log-level LEVEL 指定打印的日志等级,默认为 INFO。日志等级有 TRACE, DEBUG, INFO, WARN, ERROR, CRITICAL。如果在打包时遇到了问题,为了方便定位问题,可以使用这个参数来查看特定级别的日志信息。

  1. 数据绑定和搜索相关的参数选项:
参数名 描述 说明
--add-data SRC;DEST 指定需要添加非二进制文件路径或者文件夹路径,比如图片和 pdf 文件等,这个选项可以使用多次。这个命令其实就是将需要的文件或者文件夹拷贝到指定的路径下,在 -D 模式下,可以看情况在程序打包完成后自己手动拷贝过去。
--add-binary SRC;DEST 指定需要添加的二进制文件路径,比如 DLL 文件、动态链接库或者共享文件对象等,这个选项可以使用多次。同 -add-data 命令一样,是一个拷贝数据的功能。
-o DIR --out=DIR,指定 spec 文件的生成目录。如果没有制定,则默认使用当前的目录以生成 spec 文件。
-p DIR --paths DIR,设置导入路径 (和使用 PYTHONPATH 效果相似).可以用路径分割符 (Windows 使用分号, Linux 使用冒号)分割。

指定多个目录,也可以使用多个 -p 参数来设置多个导入路径,让 pyinstaller 自己去找程序需要的资源 5
--hidden-import MODULENAME/--hiddenimport MODULENAME 指定脚本中需要隐式导入的模块,比如在 __import__imp.find_module()execeval 等语句中导入的模块,这些模块 PyInstaller 是找不到的,需要手动指定导入,这个选项可以使用多次。
--additional-hooks-dir HOOKSPATH 指定额外 hook 文件(可以是 py 文件)的查找路径,这些文件的作用是在 PyInstaller 运行时改变一些 Python 或者其他库原有的函数或者变量的执行逻辑(并不会改变这些库本身的代码),以便能顺利的打包完成,这个选项可以使用多次。
--runtime-hook RUNTIME_HOOKS 指定自定义的运行时 hook 文件路径(可以是 py 文件),在打好包的 exe 程序中,在运行这个exe 程序时,指定的 hook 文件会在所有代码和模块之前运行,包括 main 文件,以满足一些运行环境的特殊要求,这个选项可以使用多次。
--exclude-module EXCLUDES 指定可以被忽略的可选的模块或包,因为某些模块只是 PyInstaller 根据自身的逻辑去查找的,这些模块对于 exe 程序本身并没有用到,但是在日志中还是会提示 module not found ,这种日志可以不用管,或者使用这个参数选项来指定不用导入,这个选项可以使用多次。
--key KEY 指定用于 Python 字节码加密的 keykey 是一个16个字符的字符串。
  1. WindowsMac 特有的参数
参数名 描述 说明
-c 提供一个命令行窗口进行 I/O,比如 pyinstaller -c demo.py -w 相反,默认含有此参数
-w -,程序运行后隐藏命令行窗口,比如 pyinstaller -w demo.py 编写 GUI 程序时使用此参数有用,比如当你不需要使用命令行窗口作为程序的 I/O 时;

如果想程序运行的时候与程序进行交互,则不加该参数。
-i --icon,为 main.exe 指定图标,比如 pyinstaller -i D:\icons\demo.ico demo.py 给生成的 demo.exe 文件设置一个自定义的图标 D:\icons\demo.ico

spec文件

当执行打包命令时,PyInstaller 首先建一个 sepc (specification) 文件 script.spec,默认放在当前文件夹下,默认名为 xxx.spec 配置文件。
spec 文件的作用是什么呢?生成 spec 文件时可以什么都不指定,它会告诉 PyInstaller 如何处理你的 py 文件,它会将你的 py 文件名字和输入的大部分参数进行编码。PyInstaller 通过执行 spec 文件中的内容来生成 exe,有点像 makefile

spec 文件其实就是一个 py 文件,在编辑时可以直接将它当作一个 py 文件来使用。

正常使用中我们是不需要管 spec 文件的,但是下面几种情况需要修改 spec 文件:

    1. 需要打包资源文件;
      可以在 spec 文件中单独用一个列表变量来制定,可读性和可维护性会高很多。
    1. 需要 include 一些 PyInstaller 不知道的库;
      比如 .dll 或者 .so 文件,同样可以在 spec 文件中单独用一个列表变量来制定。
    1. 为可执行文件添加一些运行时的选项,比如 run-time 选项和hook文件;
    1. 多程序打包。

若我们所需的场景符合上面所描述的任意一个,则需要在打包前生成 spec 文件并修改。生成一个纯粹的 spec 文件的命令如下:

pyi-makespec -options xxx.py [other scripts]

在这里插入图片描述
出现图片上的提示,即为生成 spec 文件成功。
生成并修改 spec 文件后,可以根据 spec 文件进行打包,具体命令如下:

pyinstaller xxx.spec

修改spec文件

spec 文件默认的结示例如下:

block_cipher = None


a = Analysis(
    ['script.py'],
    pathex=[],
    binaries=[],
    datas=[],
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    [],
    exclude_binaries=True,
    name='script',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)
coll = COLLECT(
    exe,
    a.binaries,
    a.zipfiles,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='script',
)

spec 文件是一个 python 脚本,文件中主要包含4个 class : Analysis, PYZ, EXECOLLECT 46

变量 含义
a Analysis 的实例,要求传入各种脚本用于分析程序的导入和依赖。
scripts,它是一个脚本列表,可以传入多个 py 脚本,效果与命令行中指定多 py 文件相同,即 py 文件不止一个时,比如 pyinstaller xxx1.py xxx2.pypyinstaller 会依次分析并执行,并把第一个 py 名称作为 specdist 文件下的文件夹和程序的名称;
pathex,同命令 -p DIR / --paths DIR,其实默认就有一个 spec 的目录,如果使用命令添加的话,会首先添加命令中指定的目录,再添加默认的路径。

它定义了打包的主目录(生成 spec 文件的时候会自动填充好主目录路径),对于在此目录下的 py 文件可以只写文件名不写路径。
datas,添加数据文件,命令是 --add-dataspec

文件中是 Analysisdatas = [] 参数,datas 是一个元素为元祖的列表,每个元祖都有两个元素,元祖的基本格式为(”原项目中资源文件路径” , ”打包后的文件路径”),都必须是字符串类型,元祖的第一个元素为数据文件或文件夹,元祖的第二个元素为运行时这些文件或文件夹的位置。

例如,datas = [("src/README.txt", "."), ],也可以在命令中中这样写 pyinstaller --add-data "src/README.txt; ." myscript.py,表示打包时将文件 src/README.txt 添加到相对于 spec 文件的根目录下,指定文件时是相对于 spec 来进行寻找的,而不是要打包的 exe 程序路径。

可以使用通配符 datas = [("/mygame/sfx/*.mp3", "sfx")],表示将 /mygame/sfx/ 目录下的 .mp3 文件都拷贝到 sfx 文件夹中。
也可以添加整个文件夹 datas=[("/mygame/data", "data")],表示将 /mygame/data 文件夹下所有的文件都拷贝到 data 文件夹下。
binaries,添加二进制为摁键,效果同命令 --add-binary,也是一个列表,定义方式与 datas 参数一样。
hiddenimports,同命令 --hidden-import MODULENAME/--hiddenimport MODULENAME

一般而言都是在生成可执行程序exe 的过程中抛出 No Module named xxx 错误才会发现这些遗漏的模块,碰到这些异常,只需在 spec 文件中添加对应的遗漏模块即可。
hookspath,同命令 --additional-hooks-dir HOOKSPATH
runtime_hooks,同命令 --runtime-hook RUNTIME_HOOKS
exclude,同命令 --exclude-module EXCLUDES,以排除某些用不到的模块 5
PYZ PYZ 的实例,是一个 .pyz 文件,包含程序运行需要的所有依赖;
EXE EXE 的实例,这个类用来处理 AnalysisPYZ 的结果的,也是用来生成最后的 exe 可执行程序;
console, 设置是否显示命令行窗口,和命令 -w / -c 作用一样。
icon,设置程序图标,和命令 -i / --icon 作用一样。某些情况,直接执行 pyinstaller xxx.py 时生成的 spec 中没有这个参数,需要手动添加,参数值就是图片路径的字符串。
COLL COLLECT 类的实例,用于创建输出目录。
-F 模式下,是没有 COLLECT 实例的,并且所有的脚本、模块和二进制文件都包含在了最终生成的 exe 文件中。

以下的全局变量也可以在 spec 文件中使用 4

全局变量 含义
DISPATH 相对于 dist 文件夹的相对路径,如果 --dispath 参数选项呗指定了,则使用被指定的参数值。
HOMEPATH pyinstaller 查找的绝对路径,一般是 python 解释器的 site-packages 文件夹的绝对路径。
SPEC 在命令行中指定的 spec 文件路径。
SPECHPATH spec 文件的文件名,不包括文件类型后缀。
specnm spec 文件的文件名,不含文件类型后缀。
workpath 相对于 build 文件夹的相对路径,如果 workpath=参数选项 呗指定了,这使用呗指定的值。
WARNFILE build 文件夹中警告文件的全路径。一般是 warn-myscript.txt

当命令行和 spec 中指定了相同的参数选项,那么命令行的参数选项会被忽略。

在对 python 项目进行打包修改 sepc 文件时,主要是以对 analysis 进行修改和配置为主。
以针对多脚本、多目录的 python 项目为例,一般而言对 spec 文件的配置都包含如下一些操作:

    1. py 文件打包配置
      Analysis 的第一个参数(如 [script.py])为需要解析的 py 脚本。针对多目录多脚本的 python 项目,打包时需要将所有相关的 py 脚本文件都添加到 Analysis 类里,即第一个参数中,参考 78 中给出的例子。
    1. 资源文件打包配置
      资源文件包括打包的 python 项目使用的相关文件,如模型配置文件、模型数据文件或者图标文件、文本文件等。
      对于此类资源文件的打包需要设置 Analysisdatas。比如, ('data','data') 表示将 C:\\Users\\RYW\\Desktop\\test\\data 下的所有资源文件打包后放入打包结果路径下的 data 目录中,参考 78 中给出的例子。
    1. Hidden import 配置
      pyinstaller 在进行打包时,会解析打包的 python 文件,自动寻找 py 源文件的依赖模块。但是 pyinstaller 解析模块时可能会遗漏某些模块(not visible to the analysis phase),造成打包后执行程序时出现类似 No Module named xxx。这时我们就需要在 Analysishiddenimports 中加入遗漏的模块,参考 78 中给出的例子。
    1. 递归深度设置
      在打包导入某些模块时,常会出现 RecursionError: maximum recursion depth exceeded 的错误,这可能是打包时出现了大量的递归超出了 python 预设的递归深度。因此需要在 spec 文件上添加递归深度的设置,设置一个足够大的值来保证打包的进行,即:
import sys
sys.setrecursionlimit(5000)

运行时信息:

  • __file__
    所有基于模块的使用到 __file__ 属性的代码,在代码运行时表示的是当前脚本的绝对路径,但是打包后就是当前模块的模块名(即文件名 xxx.py)。

  • sys.frozen
    源码运行时没有这个属性,打包后的程序添加了这个属性,值为True。

  • sys._MEIPASS
    源码运行时没有这个属性,打包后的程序添加了这个属性,表示程序运行的绝对路径。
    对于 -D 模式程序,表示的是这个 exe 程序所在文件夹的绝对路径;
    对于 -F 模式程序,表示的是 _MElxxxxxx 的文件夹绝对路径,_MElxxxxxxexe 解压后创建的临时运行环境的文件夹名称。对于 exe 程序每一次运行来说,它是唯一的。
  • sys.executable
    代码运行时表示运行的解释器绝对路径,比如 C:\Python36\python.exe,在打包的程序中就是 exe 程序文件的绝对路径,这个是用来定位用户运行该程序的真实位置。
    sys.argv[0]
    一般来说就是运行程序的绝对路径,但是在不同平台或者不同的方式启动程序时会有所不同,比如通过符号连接运行的 sys.argv[0] 就是符号名称,而不是真实的程序路径。

pipenv 虚拟环境

打包生成的可执行文件过大的原因可能是因为 anaconda 环境下打包时引入了很多不必要的文件。
最简办法是用 pipenv 创建纯净环境。在纯净环境下,pip 安装程序所需要的第三方库,再打包程序 9

  1. pip install pipenv
  2. pipenv --python 3.9
    在当前目录创建3.9版本的 python 环境,注意 python 3.9 是利旧,利用旧有版本的 python 编译器,创建纯净的 python 3.9 虚拟环境;
    比如,当前 python 版本是 3.9pipenv --python 3.8 就会报错,是利旧的方式,而不是新增的方式,创建虚拟环境。
    如果报 virtualenv.py: error: no such option: --creatorFailed to create virtual environment,则先使用 pip install virtualenv --upgrade,然后 virtualenv --version 确认 virtualenv 被成功安装和升级 10
  3. pipenv shell
    打开 pipenv 的命令行
  4. pip install XXX
    pipenv shell 下安装所要打包的程序用到的第三方库。
    比如 pandasopenpyxl、以及打包所用的 pyinstaller
  5. pip list
    pipenv shell 下使用 pip list 查看已有的库文件,可以看到,环境很纯净,只有第三方库及其依赖文件。
  6. pyinstaller -F xxx.py 打包。

如果期间因为某些原因操作中断后找不到此前创建的 pipenv 虚拟环境,参考这里 11 找回原有虚拟环境。


应用

简单的 .py 文件

对一个简单 .py 文件,当没有依赖的其他路径下的 .py 文件、没有间接依赖的其他包、没有依赖的资源文件时,可使用命令 pyinstaller –F xxx.py ,直接打包成一个 exe 文件。

使用命令 pyinstaller -c –F xxx.py 直接打包 xxx.py 文件。


存在前提依赖的复杂项目

一般而言,实际的项目往往是复杂的,或有许多依赖的其他路径下的 .py 文件,或有许多间接依赖的包,或有许多设计的资源文件。

针对多脚本的 python 项目可以通过修改 spec 文件的方法进行打包。

也可以通过核心命令 pyinstaller -F xxx.py -p py_dir 进行打包。但是在实际打包过程中或者即便打包成功后但是在执行可执行程序时,经常会遇到错误使得我们还是得去修改 spec 文件重新打包执行程序。因此,本文直接使用修改 spec 文件再根据 spec 文件的方法对这几种类型的 python 项目打包示例进行讲解,对于使用核心命令对多个程序进行打包的案例,可以参考这里 12,本文不再讲述。一般而言,大部分项目都会涉及多个脚本并包含数据资源文件。

spec 配置文件打包发布完毕后,必定会在 dist 中生成一个文件夹,里面不只有生成的exe 文件,还有许多其他的依赖文件。此模式下,通常使用命令 pyinstaller –D xxx.spec 进行发布。

基本步骤:

    1. 生成 spec文件
      执行命令 pyi-makespec xxx.py ,执行后生成 xxx.spec 文件( xxx.py 是项目启动的入口文件);
    1. 完善 spec 文件中的内容
    1. 根据 spec 再次进行打包

问题、处理及注意事项

参考 18

  1. 当双击 exe 文件无法看到错误信息时,可尝试通过 cmd 进入对应目录后命令运行 xxx.exe;

  2. 当打包成功,且中间没有发生任何警告提示,但是运行程序时提示某个模块找不到,可能是 --hidden-import= 的问题。对于不知道 hiddenimports 里面具体依赖的包有哪些,一个笨办法是,在 cmd 下执行xxx.exe,根据提示的错误信息,逐一加上缺失的包 7

  3. 杀毒软件可能会干预,最好打包发布前退出杀毒软件。

  4. Unable to find "nltk_data" when adding binary and data files
    这种错误发生在生成 exe 可执行程序时,会导致生成 exe 失败。这是 PyInstallerhook 文件夹在处理 nltk 时出现这样的错误,参考 7

  5. XGBoostLibraryNotFound:Connot find XGBoost Library in … 的报错,参考 7

  6. FileNotFoundError:No such file or directory:…(数据资源未能成功打包),参考 7

  7. 无论在哪个目录下执行 pyinstaller 的命令,默认打包完成的文件或文件夹就在该目录。

  8. 路径最好为英文,没有中文字符。

  9. 脚本名称里应当没有特殊字符。

  10. 使用 utf-8 编码。

  11. 图标文件必须是正常格式,为 .ico 文件。

  12. 命令使用 pyinstallerpyinstaller.exe 结果都是一样的。

  13. 参数的添加得在 pyinstallerdemo.py 文件中间,不能随意位置添加。

  14. 如果打包的代码中用到了静态文件,如图片和资源文件,需配置 spec 文件将相关文件复制到 dist 文件夹或其子文件夹下,否则 exe 文件会报找不到文件的错。

  15. 代码里导入 python 包最好使用 from * import * 的方式,而不是 import *,可以节省打包后的文件大小。因为使用 import * 时,pyinstaller 打包时会将 python 解释器及项目中使用的整个模块复制过去,此时打出来的包就会很大 13

  16. 当使用错误的参数去打包或打包到一半中断等等此类运行到一半没了的情况,会导致原来的 .py 文件变成一个 0 KB 的空文件,里面的代码会全部消失。因此,最好复制一份代码出来,用这个副本进行打包。

  17. pygame 代码调试的时候要在结束时加 quit(),不然程序会崩溃。

  18. 如果在 py 文件中用到了多进程,且在 windows 下编译需要加一行代码在开头,multiprocessing.freeze_support()

  19. 打包的时候要进入到能运行这个 .py 文件的虚拟环境中,不能在别的环境中对 .py 文件打包,不然打包的结果还是原来的环境,可能打包文件过大,或者引起缺失包等其他问题。

  20. 如果打包错误,可查看 build 里的 warn_script.txt 文档,里面详细记载了错误的原因。一般都是库丢失。

  21. 出现 IndexError: tuple index out of range,出现这个是由于当前的 pyinstaller 版本不支持 python 的版本。解决方法是修改 pyinstaller 的版本,pip install pyinstaller == 适合你的版本

  22. 出现 ImportError: C extension: No module named ‘pandas._libs.tslibs.timedeltas’,出现这个是由于代码中引入了 pandas 库但是使用 pyinstaller 打包时显示模块缺失。可以选择:
    暂时忽略此模块,即打包时加上 --hidden-import=pandas._libs.tslibs.timedeltas。或者,
    python3 的安装路径下找到 Pyinstaller/hooks 的位置,如 C:\Python36\Lib\site-packages\PyInstaller\hooks 中新建文件 hook-pandas.py 并填写以下内容:

hiddenimports = [
# all your previous hidden imports
`pandas`: `pandas._libs.tslibs.timedeltas`
]
  1. 出现 module not found 警告时,可能并不需要对此进行特殊处理。只是 Pyinstaller 根据自身的逻辑去查找相应的模块,因为它们并不是跟你的最终程序有关的。

  2. 出现 File "c:\python36\lib\site-packages\PyInstaller\hooks\pre_safe_import_module\hook-setuptools.extern.six.moves.py", line 34, in pre_safe_import_module for real_module_name, six_module_name in real_to_six_module_name.items(): AttributeError: 'str' object has no attribute 'items', 解决方案是使用 pip install --upgrade setuptools 更新第三方库 setuptools 4

  3. pyinstaller 打包的 .py 项目,路径建议使用 os.path.join,由此可以避免跨平台时由于路径表达不同导致的问题,且不会出现相对路径的问题 13

  4. 为避免程序使用的图标无法显示、程序使用的关联文件无法关联,或者,程序使用的文件路径发生改变引入的问题,可以根据执行路径进行路径 "冻结“。参考这里 14

  5. 如果本机安装了很多模块,使用 pyinstaller 打包的时候就会把已安装的模块都打包进去,从而导致打包生成的 exe 文件特别大。因此可以结合 pipenv 打包,参考这里 7

  6. 在打包导入了 pandasnumpy 模块的 .py 文件时可能出现如下的报错:

Python | 打包文件 | 打包 py 文件为可执行文件 .exe_第1张图片
按照提示先安装 openpyxl,此外还需要注意版本冲突 15,再进行打包。
此时,可以通过修改 .spec 文件对 pandasnumpy 打包 16,或者使用 pyinstaller -F --hidden-import pandas --hidden-import numpy 命令进行打包 17


示例

依赖资源文件

在同一个目录下存在两个文件,一个 script.py 文件,一个 Config.json 文件。
Script.py 程序读写作为配置文件的 Config.json 文件。

.py 文件中的方法可以使用 sys.argv[1] 以从控制台获取参数。

sys.argv[] 说白了就是一个从程序外部获取参数的桥梁,从外部取得的参数可以是多个,所以获得的是一个列表(list),也就是说 sys.argv 其实可以看作是一个列表,所以才能用 [] 提取其中的元素。其第一个元素是程序本身,随后才依次是外部给予的参数 18

Config.json 的文件内容如下,

"This is Config.json."

script.py 的内容如下,

import sys

print(sys.argv[0])
print(sys.argv[1])

项目的文件结构树如下:
Python | 打包文件 | 打包 py 文件为可执行文件 .exe_第2张图片
为确保代码可运行,可在cmd控制台中实验,如下,

python script.py Config.json

确认可传参正常运行后,准备开始打包 exe


Unix / Linux / MacOS

参考这里 19


仅打包 py 文件

  1. 进入 script.py 所在目录,使用以下命令
pyinstaller -F script.py

执行完毕后,若看到如下图所示的信息则表明打包成功:

在这里插入图片描述

执行完毕后,会有 dist 目录生成,dist 目录下有同名可执行文件 script.exe,文件结构树如下:

Python | 打包文件 | 打包 py 文件为可执行文件 .exe_第3张图片

  1. 执行以下命令,若运行过程中无报错,则打包后的 exe 可正常使用。如下图所示:

在这里插入图片描述
或者,
在这里插入图片描述
该方法需要注意 script.exeConfig.json 文件的路径,否则极易出错。


打包依赖资源文件

先打包后修改spec
  1. 进入 script.py 所在目录,使用以下命令
pyinstaller script.py

执行完毕后,若看到如下图所示的信息则表明打包成功:


执行完毕后,会有 dist 目录生成,dist 目录下有同名子文件夹 script,同名子文件夹下有同名可执行文件 script.exe,文件结构树如下:

Python | 打包文件 | 打包 py 文件为可执行文件 .exe_第4张图片
文件名称的列表如下:

Python | 打包文件 | 打包 py 文件为可执行文件 .exe_第5张图片

  1. 修改 script.spec 的内容,将 Config.json 添加到 data 中,表示将 script.py 所依赖的 Config.json 文件一同打包,如下所示:

Python | 打包文件 | 打包 py 文件为可执行文件 .exe_第6张图片
3. 执行 pyinstaller script.spec,依据 script.spec 中的内容将 Config.json 打包至 dist/script 目录下,若看到如下所示的信息则表示打包成功:

Python | 打包文件 | 打包 py 文件为可执行文件 .exe_第7张图片
文件名称的列表如下:

Python | 打包文件 | 打包 py 文件为可执行文件 .exe_第8张图片

先配置spec后打包
  1. 进入 script.py 所在目录,使用以下命令先生成 script.spec 文件
pyi-makespec script.py 

执行完毕后,若看到如下图所示的信息则表明 .spec 文件生成:

在这里插入图片描述

项目的文件结构树如下:

Python | 打包文件 | 打包 py 文件为可执行文件 .exe_第9张图片

  1. 修改 script.specdatas,以将程序所在同目录下的 Config.json 文件拷贝至打包后的程序所在根目录下,

Python | 打包文件 | 打包 py 文件为可执行文件 .exe_第10张图片

  1. 执行 pyinstaller script.spec,根据 .spec 内容对程序内容进行打包,出现以下信息时说明打包成功:
    Python | 打包文件 | 打包 py 文件为可执行文件 .exe_第11张图片
    项目的文件结构树如下:
    Python | 打包文件 | 打包 py 文件为可执行文件 .exe_第12张图片

windows

下面的内容可参考这里 20


仅打包 py 文件

  1. 确认 script.py 可以接收 Config.json 作为参数传入并且正常运行。在 pycharm 终端或者 cmd 控制台输入:
python script.py Config.json

如果可以看到正常的运行日志的输出,这说明可以从 cmd 控制台向 .py 文件中传递参数。下一步就是要打包成 exe 文件了。

  1. 进入 script.py 所在的目录,打包 script.py
pyinstaller –F C:\Users\Desktop\TEST\script.py

结果如下所示,则表明打包成 exe 成功:

6098 INFO: Writing RT_GROUP_ICON 0 resource with 104 bytes
6099 INFO: Writing RT_ICON 1 resource with 3752 bytes
6099 INFO: Writing RT_ICON 2 resource with 2216 bytes
6099 INFO: Writing RT_ICON 3 resource with 1384 bytes
6099 INFO: Writing RT_ICON 4 resource with 37019 bytes
6099 INFO: Writing RT_ICON 5 resource with 9640 bytes
6099 INFO: Writing RT_ICON 6 resource with 4264 bytes
6099 INFO: Writing RT_ICON 7 resource with 1128 bytes
6103 INFO: Copying 0 resources to EXE
6104 INFO: Emedding manifest in EXE
6104 INFO: Updating manifest in C:\Users\Desktop\TEST\dist\script.exe.notanexecutable
6105 INFO: Updating resource type 24 name 1 language 0
6109 INFO: Appending PKG archive to EXE
7934 INFO: Building EXE from EXE-00.toc completed successfully.

打包结束后在当前文件夹生成 distbuild 两个文件夹和 xxx.spec 文件,其中 dist 文件夹中便是执行文件,里面会有个 xxx.exe

检验打包后可不可以使用,可以在 Pycharm 的终端或者 cmd 窗口验证结果:

C:\Users\Desktop\TEST\dist\script.exe C:\Users\Desktop\TEST\Config.json

若运行结果中无报错,则该打包后的 exe 文件可成功运行。

需要注意地是:

  • 通过 cmd 命令行传进的参数全是字符串类型的;
  • 以上是在文件所在的文件夹下调用的 cmd,所以不需要写明文件地址,如果不是在文件所在的文件夹下,则需要加上文件路径。
  • 以上描述内容是对于从 cmd 获取参数的,对于不需要获取参数的也同样适用,只是在 cmd 中不需要写参数而已。

参考链接

tkinter 相关的示例


  1. linux python封装成exe,pyinstaller打包python文件成exe(原理.安装.问题) ↩︎ ↩︎

  2. python打包含有参数传递的exe程序 ↩︎ ↩︎ ↩︎ ↩︎

  3. python打包成可执行文件app(Mac版) ↩︎

  4. Python三方库:PyInstaller(exe程序打包) ↩︎ ↩︎ ↩︎ ↩︎

  5. pyinstaller打包exe程序步骤和添加依赖文件方法 ↩︎ ↩︎

  6. pyinstaller打包外置文件并使用 ↩︎

  7. 利用pyinstaller将python项目脚本打包成可执行文件 ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  8. 使用pyinstaller将python项目打包发布为exe可执行文件 - 探索字符串 ↩︎ ↩︎ ↩︎ ↩︎

  9. 解决pyinstaller打包文件过大的问题(Anaconda) ↩︎

  10. Installing Python environment and packages error , virtualenv.py: error: no such option: --seeder (IDFGH-6840) ↩︎

  11. 关于python:pycharm-添加已存在的-pipenv-虚拟环境解释器 ↩︎

  12. 【Python】 —— pyinstaller 打包多个 py 文件为一个 exe ↩︎

  13. linux 打包文件夹_Python利用PyInstaller打包 ↩︎ ↩︎

  14. pyinstaller 打包程序的相对路径问题 ↩︎

  15. pyinstaller打包程序包含openpyxl库问题解决 ↩︎

  16. PyInstaller and Pandas ↩︎

  17. pyinstaller打包exe,无法包含pandas、numpy解决办法 ↩︎

  18. python命令行参数sys.argv的使用 ↩︎

  19. python打包运行在Linux上 ↩︎

  20. 如何用pyinstaller将需要传参数的Python文件生成exe文件 ↩︎

你可能感兴趣的:(Python,python,开发语言)