当前位置: 首页 > 工具软件 > Nuitka > 使用案例 >

Python打包(pyinstaller&nuitka)

安经纶
2023-12-01

Python PyInstaller安装和使用教程

在创建了独立应用(自包含该应用的依赖包)之后,还可以使用 PyInstaller 将 Python 程序生成可直接运行的程序,这个程序就可以被分发到对应的 Windows 或 Mac OS X 平台上运行。

安装 PyInstalle

Python 默认并不包含 PyInstaller 模块,因此需要自行安装 PyInstaller 模块。

安装 PyInstaller 模块与安装其他 Python 模块一样,使用 pip 命令安装即可。在命令行输入如下命令:

pip install pyinstaller

强烈建议使用 pip 在线安装的方式来安装 PyInstaller 模块,不要使用离线包的方式来安装,因为 PyInstaller 模块还依赖其他模块,pip 在安装 PyInstaller 模块时会先安装它的依赖模块。

运行上面命令,应该看到如下输出结果:

Successfully installed pyinstaller-x.x.x

其中的 x.x.x 代表 PyInstaller 的版本。

在 PyInstaller 模块安装成功之后,在 Python 的安装目录下的 Scripts(D:\Python\Python36\Scripts) 目录下会增加一个 pyinstaller.exe 程序,接下来就可以使用该工具将 Python 程序生成 EXE 程序了。

PyInstaller生成可执行程序

PyInstaller 工具的命令语法如下:

pyinstaller 选项 Python 源文件

不管这个 Python 应用是单文件的应用,还是多文件的应用,只要在使用 pyinstaller 命令时编译作为程序入口的 Python 程序即可。
PyInstaller工具是跨平台的,它既可以在 Windows平台上使用,也可以在 Mac OS X 平台上运行。在不同的平台上使用 PyInstaller 工具的方法是一样的,它们支持的选项也是一样的。

下面先创建一个 app 目录,在该目录下创建一个 app.py 文件,文件中包含如下代码:

from say_hello import *
def main():
    print('程序开始执行')
    print(say_hello('孙悟空'))
# 增加调用main()函数
if __name__ == '__main__':
    main()

接下来使用命令行工具进入到此 app 目录下,执行如下命令:

pyinstaller -F app.py

执行上面命令,将看到详细的生成过程。当生成完成后,将会在此 app 目录下看到多了一个 dist 目录,并在该目录下看到有一个 app.exe 文件,这就是使用 PyInstaller 工具生成的 EXE 程序。

在命令行窗口中进入 dist 目录下,在该目录执行 app.exe ,将会看到该程序生成如下输出结果:

程序开始执行
孙悟空,您好!

由于该程序没有图形用户界面,因此如果读者试图通过双击来运行该程序,则只能看到程序窗口一闪就消失了,这样将无法看到该程序的输出结果。

在上面命令中使用了-F 选项,该选项指定生成单独的 EXE 文件,因此,在 dist 目录下生成了一个单独的大约为 6MB 的 app.exe 文件(在 Mac OS X 平台上生成的文件就叫 app,没有后缀);与 -F 选项对应的是 -D 选项(默认选项),该选项指定生成一个目录(包含多个文件)来作为程序。

下面先将 PyInstaller 工具在 app 目录下生成的 build、dist 目录删除,并将 app.spec 文件也删除,然后使用如下命令来生成 EXE 文件。

pyinstaller -D app.py

执行上面命令,将看到详细的生成过程。当生成完成后,将会在 app 目录下看到多了一个 dist 目录,并在该目录下看到有一个 app 子目录,在该子目录下包含了大量 .dll 文件和 .pyz 文件,它们都是 app.exe 程序的支撑文件。在命令行窗口中运行该 app.exe 程序,同样可以看到与前一个 app.exe 程序相同的输出结果。

PyInstaller 不仅支持 -F、-D 选项,而且也支持如表 1 所示的常用选项。

表 1 PyInstaller 支持的常用选项

-h,–help查看该模块的帮助信息
-F,-onefile产生单个的可执行文件
-D,–onedir产生一个目录(包含多个文件)作为可执行程序
-a,–ascii不包含 Unicode 字符集支持
-d,–debug产生 debug 版本的可执行文件
-w,–windowed,–noconsolc指定程序运行时不显示命令行窗口(仅对 Windows 有效)
-c,–nowindowed,–console指定使用命令行窗口运行程序(仅对 Windows 有效)
-o DIR,–out=DIR指定 spec 文件的生成目录。如果没有指定,则默认使用当前目录来生成 spec 文件
-p DIR,–path=DIR设置 Python 导入模块的路径(和设置 PYTHONPATH 环境变量的作用相似)。也可使用路径分隔符(Windows 使用分号,Linux 使用冒号)来分隔多个路径
-n NAME,–name=NAME指定项目(产生的 spec)名字。如果省略该选项,那么第一个脚本的主文件名将作为 spec 的名字
在表 1 中列出的只是 PyInstaller 模块所支持的常用选项,如果需要了解 PyInstaller 选项的详细信息,则可通过 pyinstaller -h 来查看。

遇到问题

1、pyinstaller打包报错: RecursionError: maximum recursion depth exceeded

解决方法:

在第一次打包报时会生成.spec文件, 修改打开该文件
在文件第二行添加

import sys 
sys.setrecursionlimit(5000)

将递归次数设置大一些,然后重新打包

pyinstaller *.spec

2、pyinstaller打包后运行提示找不到模块

在打包时候,并没有提示错误,可以顺利打包成exe文件。但是在运行打包好的软件时,会提示找不到模块,本人遇到的是找不到第三方模块,例如 requests 。这时候需要在打包时指定 -p 参数,后面跟上python目录下的第三方库模板目录路径 site-packages。再打包就成功了

pyinstaller example.py -F -p C:/python/lib/site-packages

参考:https://blog.csdn.net/itworld123/article/details/93051789

3、Pyinstaller打包出现UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0xce in position解决方案

在你打包的命令行中先输入chcp 65001 ,然后再输入打包命令。

pyinstaller -F xxx.py

参考:https://blog.csdn.net/qq_38343111/article/details/91362920

4、pyinstaller打包报错: RecursionError: maximum recursion depth exceeded,UnicodeDecodeError 解决办法

出现原因:

这个错误意思是超过最大递归深度,python默认的递归深度默认是1000),因此当递归深度超过就会引发这样的异常。

解决方法:

(1)执行pyinstaller -F XXX.py 它会在你的目录文件生成XXX.spec文件,然后报错,出现该类异常。

(2)打开XXX.spec文件,在开头添加上面两行代码。

import sys
sys.setrecursionlimit(5000)

(3)继续执行打包,但是还文件名:pyinstaller -F XXX.spec ,执行该文件。

参考:https://www.cnblogs.com/patrickstar2019/p/11465220.html

5、pyinstaller打包exe文件及过程中 no module named 问题处理
若打包后,运行exe报错:

ModuleNotFoundError:No module named ‘matplotlib.backends.backend_tkagg’

也就是没找到matplotlib.backends.backend_tkagg模块,解决方法是在打包命令后面加上如下:

demo.py是要打包的文件

pyinstaller -F demo.py --hidden-import matplotlib.backends.backend_tkagg

其它缺失模块也同理,把上述‘matplotlib.backends.backend_tkagg’替换成缺少的模块的名称即可。

python程序打包新姿势(nuitka)

换个方式针对python脚本进行打包, 有些时候我们写的一些脚本里面可能会包含SECRET或PASSWORD相关的信息,但是这些脚本有的时候是需要给到其他部门的小伙伴使用,这个时候如何能保障账号密码的安全性呢(当然了,反编译还是防不住的), 获取之前你使用过pyinstaller解决过类似问题,今天我们要说的并不是pyinstaller, 而是nuitka。

关于nuitka

Nuitka是用Python编写的Python编译器。支持Python 2.6、2.7、3.3、3.4、3.5、3.6、3.7和3.8等版本。将你的python程序打包成一个可执行文件。

执行时间

复杂的程序进行打包的时候耗时是比较久的,打包速度这点比不上pyinstaller

安装

1.安装C编译器(windows)

mingw64下载页面

mingw64官网

页面拉到最下面,目前最新版本为8.1.0,

x86_64-posix-sjlj
x86_64-posix-seh
x86_64-win32-sjlj
x86_64-win32-seh
i686-posix-sjlj
i686-posix-dwarf
i686-win32-sjlj
i686-win32-dwarf

附上版本区别详解供参考

64位系统建议选择x86_64-posix-sjlj

32位系统建议选择i686-posix-sjlj

官方文档中说要设置一下环境变量,实际使用过程中发现不设置也可以正常使用

2.安装nuitka

pip install nuitka
#或
conda install nuitka

使用方法

#1、 常用命令参数

--mingw64 #默认为已经安装的vs2017去编译,否则就按指定的比如mingw

--standalone #独立文件,这是必须的

--follow-imports #把开头import的文件或者模块一起打包

--windows-disable-console #没有CMD控制窗口

--recurse-all #所有的资源文件 这个也选上

--recurse-not-to=numpy,jinja2 #不编译的模块,防止速度会更慢

--output-dir=out #生成exe到out文件夹下面去

--show-progress #显示编译的进度,很直观

--show-memory #显示内存的占用

--plugin-enable=pylint-warnings #报警信息

--plugin-enable=qt-plugins #需要加载的PyQT插件

--nofollow-imports # 所有的import不编译,交给python3x.dll执行

--follow-import-to=need # need为你需要编译成C/C++的py文件夹命名

--include-package #将python的requests包打包进exe

–include-plugin-directory #可以将某文件夹里的所有文件打包进执行档中,这里的路径需要写绝对路径


2、 命令示例
python -m nuitka --follow-imports --show-progress --plugin-enable=pylint-warnings --recurse-all --output-dir=out main.py

nuitka --mingw64 --windows-disable-console --standalone --show-progress --show-memory --plugin-enable=qt-plugins --plugin-enable=pylint-warnings --recurse-all --recurse-not-to=numpy --output-dir=out index.py
#调试模式

nuitka --standalone --mingw64 --show-memory --show-progress --nofollow-imports --plugin-enable=qt-plugins --follow-import-to=lib  --output-dir=o main.py
#release 模式

nuitka --standalone --windows-disable-console --mingw64 --nofollow-imports --show-memory --show-progress --plugin-enable=qt-plugins --follow-import-to=lib --recurse-all --output-dir=o main.py
#打包完成后将相关的python 包拷贝到exe所在目录

详细说明

--version:
显示程序的版本号并退出。
-h, --help:
显示帮助信息并退出。
--standalone:
启用 standalone 独立模式来进行构建。这将允许你将创建的二进制文件传输到其他计算机上允许,并且无需依赖现有的 Python 环境。这个选项包含了这些选项:--recurse-all。你可能还想使用 --python-flag=no_site来回避site.py 模块,以节省大量的代码依赖。默认为不启用。
--python-arch=PYTHON_ARCH:
要使用的 Python 架构。为 x86 或 x86_64。默认为 Nuitka 运行时使用的架构。
--python-debug:
是否使用调试版本。默认情况下你运行的 Nuitka 很可能是非调试版。
--python-flag=PYTHON_FLAGS:
要使用的 Python flag 标志。默认使用你用来运行 Nuitka 的内容,这将强制使用特定的模式。这些选项也存在于标志 Python 可执行文件中。当前支持:-S(别名: nosite),static_hashes(不使用哈希随机化),no_warnings(不提供 Python 运行时警告),-O(别名:noasserts)。默认为空。
--python-for-scons=PYTHON_SCONS:
如果使用 Python3.3 或 Python3.4,提供用于 Scons 的 Python 二进制文件路径。除此之外,Nuitka 还能使用运行 Nuitka 时使用的文件,或者使用 PATH 中的 Scons 二进制文件,或者使用 Windows 注册表中的 Python 安装。
--warn-implicit-exceptions:
对编译时检测到的隐式异常启用警告。
--warn-unusual-code:
对编译时检测到的异常代码启用警告。
--assume-yes-for-downloads:
如果有必要,允许 Nuitka 下载代码。例如,Windows 下的依赖遍历。

控制模块和包的包含

--include-package=PACKAGE:
包含一个包。提供一个 Python 命名空间,比如:some_package.sub_package,然后 Nuitka 会找到它,并将磁盘中的它和它创建的二进制文件或所有扩展模块包括在内,并通过代码将其导入。默认为空。
--include-module=MODULE:
包含一个模块。和 --include-package 一样。默认为空。
--include-plugin-directory=MODULE/PACKAGE:
包含目录的内容,不管给定的主程序是否以可见的形式使用它。将重写其他所有递归选项。可以多次使用。默认为空。
--include-plugin-files=PATTERN:
包含所有匹配的文件。将覆盖所有递归选项。可以多次使用。默认为空。

控制导入模块的递归

--follow-stdlib, --recurse-stdlib:
从标准库中导入模块。这将大大增加编译时间。默认关闭。
--nofollow-imports, --recurse-none:
当使用 --recure-none,将完全不导入任何模块,将覆盖所有其他的递归选项。默认关闭。
--follow-imports, --recurse-all:
当使用 --recurse-all 时,将导入所有模块。默认关闭。
--follow-import-to=MODULE/PACKAGE, --recurse-to=MODULE/PACKAGE:
递归指定的模块或包,可以多次使用。默认为空。
--nofollow-import-to=MODULE/PACKAGE, --recurse-not-to=MODULE/PACKAGE:
不递归指定的模块或包,将覆盖递归选项。可以多次使用。默认为空

编译后立即执行

--run:
立即执行创建的二进制文件(或导入编译的模块)。默认关闭。
--debugger, --gdb:
在 gdb 内执行,以自动获取堆栈跟踪。默认关闭。
--execute-with-pythonpath:
当立即执行创建的二进制文件时(--execute),不要重置 PYTHONPATH。当所有模块都成功包含时,你应该不再需要 PYTHONPATH。

内部树的转存选项

--xml:
将优化的最终结果转储为 XML,然后退出。

代码生成选项

--full-compat:
强制与 CPython 绝对兼容。甚至不允许与 CPython 的行为有细微的偏差。这仅用于测试,不建议正常使用。
--file-reference-choice=FILE_REFERENCE_MODE:
选择 __file__ 的值。使用 runtime(独立二进制模式和模块模式的默认值),创建的二进制文件和模块将使用它们自身的位置来代替 __file__ 的值。如果你追求速度,那么推荐使用 original 值。

输出选项

-o FILENAME:
指定应如何命名可执行文件。对于扩展模块而言,没有选择也没有独立模式,使用它将是一个错误。这可能包含需要存在的路径信息,默认为平台下的  程序名称。平台名称.exe。
--output-dir=DIRECTORY:
指定最终文件的输出目录。默认为当前目录。
--remove-output:
生成模块或 exe 文件之后删除生成目录。默认关闭。
--no-pyi-file:
不要为创建的扩展模块创建 .pyi 文件。这用于检测隐式导入。默认为关闭。

调试特性

--debug:
执行所有可能的自检以查找 Nuitka 中的错误,不适合生成环境。默认关闭。
--unstripped:
在生成的对象文件中保留调试信息,以便更好地进行调试器交互。默认关闭。
--profile:
启用基于 vmprof 的时间分析。默认关闭。
--graph:
创建优化过程图。默认关闭。
--trace-execution:
跟踪执行输出,在执行前输出代码行。默认关闭。
--recompile-c-only:
这不是增量编译,而是仅用于 Nuitka 开发。获取现有文件并将其重新编译为 C。允许编译编辑过的 C 文件,以便快速调试对生成源的更改,例如查看代码是否通过、值的输出等。默认关闭。
--generate-c-only:
只生成 C 源代码,不编译生成二进制文件或模块。这是为了调试和代码覆盖率分析,而不是浪费 CPU。默认关闭。
--experimental=EXPERIMENTAL:
使用声明为 experimental 的功能。如果代码中不存在任何实验功能,则可能没有效果。
--disable-dll-dependency-cache:
禁用依赖项的缓存。这将导致创建分发文件夹的时间更加长。可以在怀疑缓存导致错误时使用。
--force-dll-dependency-cache-update:
用于更新依赖性缓存。这将导致创建分发文件夹的时间更加长。可以在怀疑缓存导致错误或已知需要更新缓存时使用。

后端 C 编辑器的选择

--clang:
强制使用 clang。在 Windows 上,这需要一个 Visual Studio 版本来支持。默认关闭。
--mingw64:
在 Windows 上强制使用 MinGW64。默认关闭。
--msvc=MSVC:
在 Windows 上强制使用特定的 MSVC 版本。默认为最新版本。
-j N, --jobs=N:
指定允许的并发 C 编译器的作业数量。默认为系统 CPU 的个数。
--lto:
如果可用,请使用链接时间优化(GCC 4.6 及更高版本 )。默认关闭。

跟踪特性

--show-scons:
在非安静模式下操作 Scons,显示执行的命令。默认关闭。
--show-progress:
提供进度信息和统计数据。默认关闭。
--show-memory:
提供内存信息和统计数据。默认关闭。
--show-modules:
提供包含模块的最终汇总信息。默认关闭。
--verbose:
输出所采取操作的详细信息,特别是在优化中。默认关闭。

Windows 特定控制

--windows-dependency-tool=DEPENDENCY_TOOL:
在为 Windows 编译时,使用此依赖性工具。默认为 depends.exe 文件,其他允许的值为 pefile。
--windows-disable-console:
在为 Windows 编辑时,禁用控制台窗口。默认关闭。
--windows-icon=ICON_PATH:
添加可执行文件的图标(只有 Windows 下)

插件控制

--plugin-enable=PLUGINS_ENABLED, --enable-plugin=PLUGINS_ENABLED:
启用插件。必须指定插件名。使用 --plugin-list 查询所有的插件列表并退出。默认为空。
--plugin-disable=PLUGINS_DISABLED, --disable-plugin=PLUGINS_DISABLED:
禁用插件。必须指定插件名。
--plugin-no-detection:
插件会检测是否可以使用他们。使用此选项可以禁用插件发出的警告。默认关闭。
--plugin-list:
显示所有可用的插件并退出。
--user-plugin=USER_PLUGINS:
用户插件的文件名,可以指定多次。默认为空。

打包模块与follow import

上面的命令中使用了参数–follow-import-to,这个参数位于Control the recursion into imported modules这一部分,这部分参数一共有五个

--follow-stdlib, --recurse-stdlib
                    Also descend into imported modules from standard
                    library. This will increase the compilation time by a
                    lot. Defaults to off.
--nofollow-imports, --recurse-none
                    When --recurse-none is used, do not descend into any
                    imported modules at all, overrides all other recursion
                    options. Defaults to off.
--follow-imports, --recurse-all
                    When --recurse-all is used, attempt to descend into
                    all imported modules. Defaults to off.
--follow-import-to=MODULE/PACKAGE, --recurse-to=MODULE/PACKAGE
                    Recurse to that module, or if a package, to the whole
                    package. Can be given multiple times. Default empty.
--nofollow-import-to=MODULE/PACKAGE, --recurse-not-to=MODULE/PACKAGE
                    Do not recurse to that module name, or if a package
                    name, to the whole package in any case, overrides all
                    other options. Can be given multiple times. Default
                    empty.

这一部分参数可以说是nuitka的核心。nuitka能够根据py文件中的import语句找到所有引用的库,然后将这些库文件打包进二进制文件中。找到import,然后follow,所以是follow import。所有被导入的库可以看作一个列表,而这部分参数的作用就是让用户在这个列表中进行选择,只有被选中的部分会被打包进exe

全选

--follow-imports, --recurse-all

不选

--nofollow-imports, --recurse-none

仅选择标准库

--follow-stdlib, --recurse-stdlib

仅选择指定模块/包

--follow-import-to=MODULE/PACKAGE, --recurse-to=MODULE/PACKAGE

不选择指定模块/包,这个选项会覆盖其他递归选项,也就是说最后用

--nofollow-import-to=MODULE/PACKAGE, --recurse-not-to=MODULE/PACKAGE

如果某些库没有被打包进exe,程序仍会试图通过python3x.dll去搜索路径中查找这些库,然后进行调用,调用方式跟py文件一模一样。

nuitka打包相对来说是比较耗费时间的,特别是针对像pandas这样的大家伙,所以在最终发布之前,可以暂时不对这些库进行打包(–nofollow-imports),而是将这些库手动拷贝到搜索路径中,比如exe同级目录。只要能够找到这些库,程序就能正常运行,否则会提示no module named xxx

注意:这部分参数仅仅能够处理py或者pyc文件,如果遇到pyd或者dll则会跳过

简单使用

python -m nuitka --standalone  main.py

# 执行结果差异如下

hello-world-demo: python main.py
Talk Hello World


hello-world-demo: ./main.dist/main
Talk Hello World
hello-world-demo:

来个复杂的带配置文件的

原始代码我就不贴了,功能是用来添加阿里云站点监控功能的,之前尝试使用pyinstaller来进行打包,但是么有成功,失败的原因是因为aliyunsdkcore依赖了etry_config.json文件, 而pyinstaller并不能去获取到,我尝试添加目录也没有成功,今天试下nuitka看看是否能够成功

# 打包前执行效果

aliyun-alert(master) ✗: python alertadd.py www.baidu.com
{"RequestId":"D1DD68D4-7D93-2C94-A362-DF9F1E8C46F4","Data":"29884879351CF69959852ABFC269EFEB26564237","Code":"200","Success":true}
{"RequestId":"048AF305-EC0A-4D3A-8722-C6D2FA163E46","Data":"7A2E57F9E7D1FF3ED242163231A403C526564237","Code":"200","Success":true}


# 打包

python -m nuitka --follow-imports --include-plugin-directory=/aliyun-alert alertadd.py


# 打包后执行效果

aliyun-alert(master) ✗: ./alertadd.bin www.baidu.com

{"RequestId":"E8D95559-9133-4A5E-AEC0-70581DB87A21","Data":"D64121A091FB85A818DB42268E8B5D3F26564254","Code":"200","Success":true}
aliyun-alert(master) ✗:

其他同类工具
nuitka工具的出色程度超出了我的预料(同样也是支持win的),哈哈,是真的香甜可口,除了我们前面提到的工具之后还有什么其他的么?同类工具对比图片

SolutionWindowsLinuxOSXPython3LicenseOne-file modeZipfile importEggspkg_resources supportLatest release date
bbFreezeyesyesyesnoMITnoyesyesyesJan 20,2014
py2exeyesnonoyesMITyesyesnonoOct 21,2014
pyInstalleryesyesyesyesGPLyesnoyesnoJul 9,2019
cx_FreezeyesyesyesyesPSFnoyesyesnoAug 29,2019
py2appyesnoyesyesMITnoyesyesyesMar 25,2019

引用链接

[1] Nuitka对应的github地址: https://github.com/Nuitka/Nuitka

[2] Nuitka官网: http://nuitka.net/

[3] 同类工具对比图片来源: https://docs.python-guide.org/shipping/freezing/

 类似资料: