Python打包(pyinstaller&nuitka)

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的),哈哈,是真的香甜可口,除了我们前面提到的工具之后还有什么其他的么?同类工具对比图片

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/

你可能感兴趣的:(python)