目录
- Python是解释型语言,Flask或Django项目如果部署,源码可能会泄露,因此可以把项目打包成exe,来保护源码。
-
需要用到的工具:
- pyinstaller:把python项目打包成不同平台的可执行文件
- nsis:NSIS(Nullsoft Scriptable Install System)是一个开源的 Windows 系统下安装程序制作程序,它提供了安装、卸载、系统设置、文件解压缩等功能。这如其名字所指出的那样,NSIS 是通过它的脚本语言来描述安装程序的行为和逻辑的
打包Flask项目
写一个简单的Flask项目
下载pyinstaller
pip install pyinstaller
可选参数 | 示例 | 说明 |
---|---|---|
|
|
只在dist文件夹中生成一个程序demo.exe文件,适用于一个模块没有多依赖.py文件 |
|
|
默认选项,除了主程序demo.exe外,还会在在dist文件夹中生成很多依赖文件,推荐使用这个 |
|
|
默认选项,只对windows有效,使用控制台 |
|
|
只对windows有效,不使用控制台 |
|
|
设置导入路径 |
|
|
给生成的demo.exe文件设置一个自定义的图标 |
进入到项目路径下,执行
# run.py 是flask项目的执行文件,app.run所在py文件
pyinstaller -D run.py
# 可以看到项目路径下有
-build文件夹
-dist文件夹:重要,下有run文件夹(py文件名字),有个run.exe(py文件的名字)
-run.spec
# 把pro_flask这个文件夹,拷贝到dist下的run文件夹
-因为flask项目有静态文件和html文件,如果不拷贝过去,静态文件和html文件会找不到
-我们为了隐藏代码,可以把所有python的代码都删除,只留static和templates文件夹
运行exe,测试
# 到dist/run文件夹下,双击 run.exe,启动
# 浏览器访问
使用nsis把文件夹打包成windows的安装包
下载安装nsis
nsis:NSIS(Nullsoft Scriptable Install System)是一个开源的 Windows 系统下安装程序制作程序,它提供了安装、卸载、系统设置、文件解压缩等功能。这如其名字所指出的那样,NSIS 是通过它的脚本语言来描述安装程序的行为和逻辑的
利用 nsis 把刚刚的dist的run文件夹打包成windows的安装包
把dist文件夹下的run文件夹压缩成zip
使用nsis把压缩包,做成windows安装文件
打包Django项目
写一个简单的django项目,准备静态文件
# 在django项目的配置文件中修改settings.py
STATIC_ROOT = os.path.join(BASE_DIR, 'static', 'static_root')
# 修改urls.py
from django.conf.urls import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.index),
]
urlpatterns += static.static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
# 执行 静态文件收集
python manage.py collectstatic # 会把所有静态文件收集到项目的static路径下的static_root文件夹下
使用 pyinstaller打包
# 进入到项目路径下
pyinstaller -D manage.py
# 生成dist文件夹,build文件夹和manage.spec文件
执行打包成的exe
# 此时在项目路径下会生成dist文件夹,内部manage文件夹下有manage.exe
manage.exe runserver
# 运行服务是会提示No module named XXX
执行会报错,提升缺少模块,我的提示少app01.apps
#########
这是因为Django有些module不会自动收集,需要手动添加
解决方法:在manage.spec文件中修改hiddenimports=[]为hiddenimports=['app01.apps','users.apps',],提示缺少什么module就在此处添加什么。
#########
修改manage.spec,hiddenimports=['app01.apps',]
# 再重新编译一下,运行
pyinstaller manage.spec
把templates和static文件夹copy到dist/manage文件夹下
运行项目
manage.exe runserver 0.0.0.0:8080
注意一些坑
# 问题1:运行服务提示No module named XXX
这是因为Django有些module不会自动收集,需要手动添加
解决方法:在manage.spec文件中修改hiddenimports=[]为hiddenimports=['users','users.apps',],提示缺少什么module就在此处添加什么。
# 问题2:打开网页出现TemplateDoesNotExist 错误
解决方法:把项目中的模板文件templates拷贝到dist下的manage文件夹,刷新页面即可。
# 问题3:静态文件找不到
1、首先在项目中的settings文件中添加如下代码,其中static是项目中的静态文件位置,static_root是static下的一个空文件夹,然后执行python manage.py collectstatic命令将静态文件收录到static_root中
STATIC_ROOT = os.path.join(BASE_DIR, 'static', 'static_root')
2、然后在urls.py中添加如下代码:
from django.conf.urls import static
from project_1 import settings
urlpatterns += static.static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
打包其他项目(小工具)
准备一个多文件的小工具源码
注意路径处理,使用
__file___
的地方,需要改成
"./"
,否则目录会创建到AppData的临时目录中,AppData是windows规定给软件倒垃圾的地方,但这与需求不符合。
# 目录
input_excel_folder = os.path.join("./", "in", "excel")
input_word_folder = os.path.join("./", "in", "word")
# 模板目录
template_folder = os.path.join("./template")
# 文件输出目录
out_folder = os.path.join("./", "out")
# 临时目录
tmp_folder = os.path.join("./", "out", "tmp")
执行打包程序生成.spec配置文件
pyinstaller -F main.py
修改.spec配置文件
执行
main.exe
文件,提示缺少什么就在spec中加什么。
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
# 我的项目导入了runner,interface文件夹下面的包,需要在此处添加
datas=[("runner/*","runner"),("interface/*","interface")],
# 我的项目导入了第三方包,需要在此添加
hiddenimports=["runner","docx","docxtpl","xlrd","tkinter","jinja2","matplotlib"],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
重新打包
pyinstaller main.spec
最终生成的包大小为:
将exe文件和模板文件单独放到一个文件夹,此时已经可以正常使用。(打印help乱码不影响使用)
.spec脚本参数介绍
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['main.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='main',
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='main',
)
生成的脚本就是这样的,包含以下主要配置参数:
参数 | 含义 |
a | Analysis类的实例,主要分析.py文件的依赖项,如第三方库以及import模块。a中内容主要包括以下四部分:scripts,放入.py文件。;pure,程序代码文件中的纯Python模块,包括程序的代码文件本身;binaries,需要的二进制文件。;datas,非二进制文件。 |
pyz | PYZ的实例,是一个.pyz文件,包含了所有pure中的所有Python模块。 |
exe | EXE类的实例,处理Analysis和PYZ的结果的,用来生成最后的exe可执行程序。 |
coll | COLLECT类的实例,用于创建输出目录。只有-D命令下才会实例化,-F不会生成目录。 |
block_cipher | 加密密钥(一般无加密需求,可不设置) |
而我们一般在配置时大多数情况下只涉及a,也就是Analysis类,下面逐一分析a里面的参数
Analysis的参数 | 简介以及可能存在的问题 |
scripts |
首先是一个列表(list),应该存放与打包文件相关的所有.py文件。如果不存放的话会出现import错误 no |
pathex |
默认有一个spec的目录,记得把用到的模块的路径添加到这个list变量里。 默认的路径是你打包的.py文件的同级路径。 存在问题:当引入自己的文件时添加此路径并不起作用。仍会报错
|
datas |
将资源文件或文件夹,复制到打包后的目录中,而datas中必须以元组形式否则会出现下面错误。注意一点放到这里面的文件不会被编译,而是复制,所以重要的文件放到这里很容易泄露。
|
binaries | 添加二进制文件,也是一个列表,定义方式与datas参数一样。 |
hiddenimports | 隐式导入的模块,比如在__import__、imp.find_module()、exec、eval等语句中导入的模块,这些模块PyInstaller是找不到的,需要手动指定导入, |
hookspath | 指定额外hook文件(可以是py文件)的查找路径。 |
runtime_hooks | 指定自定义的运行时hook文件路径(可以是py文件) |
excludes | 指定可以被忽略的可选的模块或包。 |
其他打包工具(cx_Freeze,PyOxidizer,Py2exe,Briefcase)
-
PyInstaller:PyInstaller 是一个流行的 Python 打包工具,可以将 Python 应用程序打包成单个可执行文件。PyInstaller 会自动识别和打包 Python 应用程序的所有依赖项,并支持多平台,包括 Windows、Linux 和 macOS。它使用 Python 语言编写,易于安装和使用。此外,PyInstaller 还提供了一些高级选项,如优化生成的可执行文件的大小和性能等。
-
cx_Freeze:cx_Freeze 是另一个流行的 Python 打包工具,可以将 Python 应用程序转换为可执行文件或打包成 ZIP 文件。与 PyInstaller 不同,cx_Freeze 可以生成 Windows、Linux 和 macOS 上的可执行文件。cx_Freeze 使用 Python 语言编写,易于安装和使用。它可以自动识别和打包 Python 应用程序的所有依赖项,并支持多个 Python 版本。
-
PyOxidizer:PyOxidizer 是一个新兴的 Python 打包工具,它使用 Rust 编写,旨在提供高度优化的可执行文件。PyOxidizer 具有良好的跨平台支持,并支持将 Python 代码打包成单个可执行文件或动态链接库。PyOxidizer 还提供了一些高级选项,如动态链接库预热、运行时优化等。PyOxidizer 不仅支持 Python 语言,还支持 Rust 和 C 语言。
-
Py2exe:Py2exe 是一个古老但仍然有用的 Python 打包工具,可以将 Python 应用程序转换为 Windows 可执行文件。Py2exe 可以自动识别和打包所有依赖项,并提供一些选项来优化生成的可执行文件的大小和性能。Py2exe 由于只支持 Windows 平台,因此在 Windows 上表现最佳。Py2exe 使用 Python 语言编写,易于安装和使用。
pyinstaller
优点:
- PyInstaller 具有广泛的操作系统支持,可以在 Windows、Linux 和 macOS 上打包应用程序。
- PyInstaller 可以自动识别和打包 Python 应用程序的所有依赖项,从而减少了配置和管理的工作量。
- PyInstaller 可以生成单个可执行文件,这使得分发和安装 Python 应用程序变得简单和方便。
- PyInstaller 支持多个 Python 版本,并且易于安装和使用。
- PyInstaller 提供了许多选项,如优化生成的可执行文件的大小和性能等。
缺点:
- 生成的可执行文件的体积可能较大。
- 在某些情况下,PyInstaller 可能无法识别 Python 应用程序的所有依赖项,从而导致运行时错误。
- 需要在不同的操作系统上生成不同的可执行文件,这可能增加了开发和测试的工作量。
cx_Freeze
优点:
- cx_Freeze 支持多个操作系统,包括 Windows、Linux 和 macOS。
- cx_Freeze 可以将 Python 应用程序转换为可执行文件或打包成 ZIP 文件,这使得分发和安装 Python 应用程序变得简单和方便。
- cx_Freeze 可以自动识别和打包 Python 应用程序的所有依赖项,从而减少了配置和管理的工作量。
- cx_Freeze 提供了许多选项,如优化生成的可执行文件的大小和性能等。
- cx_Freeze 支持多个 Python 版本,并且易于安装和使用。
缺点:
- 在某些情况下,cx_Freeze 可能无法识别 Python 应用程序的所有依赖项,从而导致运行时错误。
- 生成的可执行文件可能较大。
- cx_Freeze 缺少对一些高级功能的支持,如动态链接库预热和运行时优化等。
PyOxidizer
优点:
- PyOxidizer 提供了高度优化的可执行文件,这使得生成的应用程序在性能和体积方面表现优异。
- PyOxidizer 具有良好的跨平台支持,支持将 Python 代码打包成单个可执行文件或动态链接库。
- PyOxidizer 提供了一些高级选项,如动态链接库预热和运行时优化等。
- PyOxidizer 不仅支持 Python 语言,还支持 Rust 和 C 语言。
- PyOxidizer 可以自动识别和打包 Python 应用程序的依赖项,从而减少了配置和管理的工作量。
缺点:
- PyOxidizer 只支持 Python 3.6 或更高版本,不支持 Python 2.x。
- PyOxidizer 的配置比较复杂,需要使用 Rust 编程语言进行构建和配置。
- PyOxidizer 缺少一些常见的选项,如生成调试符号和管理包含资源的应用程序。
Py2exe
优点:
- Py2exe 支持将 Python 应用程序转换为 Windows 上的可执行文件。
- Py2exe 可以自动识别和打包 Python 应用程序的所有依赖项,从而减少了配置和管理的工作量。
- Py2exe 支持多个 Python 版本,并且易于安装和使用。
- Py2exe 提供了一些高级选项,如优化生成的可执行文件的大小和性能等。
缺点:
- Py2exe 只能在 Windows 上运行,并且只能将 Python 应用程序转换为 Windows 上的可执行文件。
- 生成的可执行文件可能较大。
- Py2exe 缺少对一些高级功能的支持,如动态链接库预热和运行时优化等。
Q&A
打包过程找不到自己自建模块
ModuleNotFoundError: No module named 'core'
可以将模块添加到datas里注意复制后的名字(‘core’,‘core’)最好是原名(推荐)。
还可以将自己的模块直接复制到site-packages 下面,再打包(不推荐)。
.spec文件
.spec文件是PyInstaller的配置文件,它包含了构建可执行文件时的各种配置选项。在PyInstaller打包的过程中,可以手动编辑.spec文件以更改某些选项。
使用以下命令生成.spec文件:
pyinstaller --name=myapp --onefile main.py
可以将–name选项替换为想要的应用程序名称,并将main.py替换为主脚本文件名。
Analysis Options
这一部分指定应用程序的入口点(通常是main.py文件),并包含一些其他配置选项,例如要添加的路径、要排除的模块等等。
a = Analysis(['main.py'],
pathex=['/path/to/myapp'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
Build Options
这一部分包含与构建可执行文件有关的选项,例如输出文件名、是否要创建调试版本等等。
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
name='myapp',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
upx_include=[],
console=True )
Deployment Options
这一部分包含与部署可执行文件有关的选项,例如生成的可执行文件是否要包含所有必需的依赖项、是否要创建单个文件等等。
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
一个应用有多个文件夹要如何打包成一个exe且不要多余的依赖文件
打包多个文件夹成一个exe文件,可以将多个文件夹中的Python文件和资源文件都复制到同一个目录下,然后使用PyInstaller打包该目录。
以下是一个示例.spec文件,假设有两个文件夹runner和helper都需要打包。
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['main.py'],
pathex=['path/to/main.py'],
binaries=[],
datas=[('runner/*', 'runner'), ('helper/*', 'helper')],
hiddenimports=[],
hookspath=[],
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='myapp',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True )
其中,datas参数指定了需要打包的文件夹及其对应的在exe文件中的路径,(‘runner/
‘, ‘runner’)表示将runner文件夹下的所有文件打包,并在exe文件中创建runner文件夹。同理,(‘helper/
’, ‘helper’)表示将helper文件夹下的所有文件打包,并在exe文件中创建helper文件夹。
使用命令pyinstaller yourapp.spec即可打包应用,并且不会有多余的依赖文件。