在创建了独立应用(自包含该应用的依赖包)之后,还可以使用 PyInstaller 将 Python 程序生成可直接运行的程序,这个程序就可以被分发到对应的 Windows 或 Mac OS X 平台上运行。
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 选项 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脚本进行打包, 有些时候我们写的一些脚本里面可能会包含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)
页面拉到最下面,目前最新版本为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的),哈哈,是真的香甜可口,除了我们前面提到的工具之后还有什么其他的么?同类工具对比图片
Solution | Windows | Linux | OSX | Python3 | License | One-file mode | Zipfile import | Eggs | pkg_resources support | Latest release date |
---|---|---|---|---|---|---|---|---|---|---|
bbFreeze | yes | yes | yes | no | MIT | no | yes | yes | yes | Jan 20,2014 |
py2exe | yes | no | no | yes | MIT | yes | yes | no | no | Oct 21,2014 |
pyInstaller | yes | yes | yes | yes | GPL | yes | no | yes | no | Jul 9,2019 |
cx_Freeze | yes | yes | yes | yes | PSF | no | yes | yes | no | Aug 29,2019 |
py2app | yes | no | yes | yes | MIT | no | yes | yes | yes | Mar 25,2019 |
引用链接
[1] Nuitka对应的github地址: https://github.com/Nuitka/Nuitka
[2] Nuitka官网: http://nuitka.net/
[3] 同类工具对比图片来源: https://docs.python-guide.org/shipping/freezing/