Pyinstaller使用以及配置文件编写
目录
- PyInstaller 使用以及配置文件编写
1. 什么是 PyInstaller
1.1 PyInstaller 简介
一句话来说:PyInstaller 是一个将 Python 程序打包成独立可执行文件的工具,让设备上没有 Python 环境的用户也能运行你的程序。
PyInstaller是怎么实现的?
- 分析依赖:找出程序需要哪些库
- 收集文件:把所有需要的 Python 库、数据文件、DLL(动态链接库)都收集起来
- 打包成 EXE:把所有东西打包成一个可执行文件或文件夹
- 添加启动器:创建一个
.exe文件,让 Windows 能直接运行
1.2 PyInstaller 的常用指令
pyinstaller your_script.py ##最简单的打包这是最基础的命令。它会生成一个 dist 文件夹,其中包含一个可执行文件和所有依赖文件(多文件模式)。
-F, --onefile #单文件模式 将所有文件(包括依赖项)打包成单个可执行文件。分发最方便,但启动速度可能略慢。
-w, --windowed #无控制台模式
-c, --console #控制台模式
--distpath DIR #指定输出目录
--workpath WORKPATH #指定临时目录
--clean #清理缓存
-H NAME, --hidden-import NAME #添加隐藏导入 当程序使用动态或延迟导入时,PyInstaller 可能遗漏。使用此参数手动强制包含。
--add-data <SRC:DEST> #添加数据文件
--add-binary <SRC:DEST> #添加二进制文件每次都使用较长的命令行参数会很麻烦,所以我们可以使用配置文件(spec文件)来简化打包过程。
2. PyInstaller 配置文件(spec 文件)
2.1 什么是 spec 文件
一句话来说:spec 文件是 PyInstaller 的配置文件,定义了如何打包你的 Python 程序。
为什么要用 Spec 文件?
- 简化命令行:只需运行
pyinstaller your_script.spec,而不是每次都输入长命令。 - 灵活配置:可以详细指定打包选项,如添加数据文件、隐藏导入等。
- 可重复使用:保存打包配置,方便以后再次打包。
2.2 Spec 文件结构
一个完整的 Spec 可以分为 4 个主要阶段:
# -*- mode: python ; coding: utf-8 -*- 告诉系统这是个 Python 脚本、支持中文字符
# 第一阶段:Analysis - 分析依赖
a = Analysis(
['main.py'], # 入口文件
datas=[...], # 数据文件
binaries=[...], # 二进制文件
hiddenimports=[...], # 隐藏导入
# ... 更多配置
)
# 第二阶段:PYZ - 压缩 Python 代码
pyz = PYZ(
a.pure, # 纯 Python 模块
a.zipped_data, # 压缩数据
)
# 第三阶段:EXE - 生成可执行文件
exe = EXE(
pyz, # 压缩后的代码
a.scripts, # 启动脚本
name='程序名', # EXE 名称
icon='icon.ico', # 图标
# ... 更多配置
)
# 第四阶段:COLLECT - 收集所有文件
coll = COLLECT(
exe, # 可执行文件
a.binaries, # 二进制文件
a.datas, # 数据文件
name='程序文件夹名', # 输出文件夹
)2.3 Analysis 分析阶段
分析依赖是最核心、最复杂的部分。主要参数包括:
a = Analysis(
['main.py'], # 入口脚本(必需)
pathex=[], # 额外的搜索路径
binaries=[], # 二进制文件列表
datas=[], # 数据文件列表
hiddenimports=[], # 隐藏导入列表
hookspath=[], # 钩子文件路径
hooksconfig={}, # 钩子配置
runtime_hooks=[], # 运行时钩子
excludes=[], # 排除的模块
win_no_prefer_redirects=False, # Windows 重定向
win_private_assemblies=False, # Windows 私有程序集
cipher=None, # 加密(一般不用)
noarchive=False, # 是否不压缩
)2.3.1 入口脚本 (scripts)
作用:告诉 PyInstaller 从哪个文件开始分析。
单个入口:
a = Analysis(['main.py'])多个入口(不常用):
a = Analysis(['main.py', 'helper.py'])2.3.2 数据文件 (datas) 与二进制文件 (binaries)
数据文件作用:打包非 Python 代码的文件(图片、配置、模型等)。
二进制文件作用:打包 DLL、SO、DYLIB 等动态链接库。
格式:(源路径, 目标路径)
示例:
datas=[
('data/config.json', 'config'), # 将 config.json 放在 dist/config/
('assets', 'assets'), # 整个 assets 目录
],
# 只在文件存在时才打包
if os.path.exists('optional_config.json'):
datas.append(('optional_config.json', '.'))
binaries = [
('C:/path/to/libexpat.dll', '.'), # 打包到根目录
]
# 从 conda 环境收集 DLL
conda_prefix = sys.prefix
library_bin = os.path.join(conda_prefix, 'Library', 'bin')
required_dlls = ['libexpat.dll', 'zlib.dll', 'sqlite3.dll']
for dll_name in required_dlls:
dll_path = os.path.join(library_bin, dll_name)
if os.path.exists(dll_path):
binaries.append((dll_path, '.'))
print(f"添加 DLL: {dll_name}")2.3.3 隐藏导入 (hiddenimports)
作用:手动添加 PyInstaller 分析不出来的模块。
什么时候需要隐藏导入?
- 动态导入(用字符串导入)
- 插件系统
- 可选依赖
- C 扩展模块
示例:
#动态导入的模块
# 代码中这样写:
module_name = "cv2"
cv2 = __import__(module_name)
# Spec 中要添加:
hiddenimports = ['cv2']
#包的所有子模块
from PyInstaller.utils.hooks import collect_submodules
hiddenimports = collect_submodules('rapidocr_onnxruntime')
# 会自动收集:
# - rapidocr_onnxruntime.models
# - rapidocr_onnxruntime.utils
# - rapidocr_onnxruntime.xxx
# ... 等所有子模块2.3.4 钩子路径 (hookspath)
钩子(Hook)可以帮助 PyInstaller 正确处理复杂的库。
钩子文件必须命名为 hook-<包名>.py:
hook-onnxruntime.py- 处理 onnxruntime 包hook-tkinter.py- 处理 tkinter 包
钩子路径作用:指定自定义钩子文件的目录。
2.3.5 排除模块 (excludes)
作用:明确告诉 PyInstaller 不要打包某些模块。
为什么要排除?
- 减小体积 - 排除不用的大型库
- 避免冲突 - 排除可能引起问题的库
- 加快打包 - 少分析一些模块
示例(具体排除什么请根据项目来裁定):
excludes = [
# 测试工具
'pytest', 'IPython', 'unittest',
# 不用的 GUI 框架
'PyQt5', 'PyQt6', 'PySide2', 'PySide6',
'wx', 'wxPython',
# 不用的深度学习框架
'paddle', 'paddlepaddle', 'paddlex', # 783 MB!
'torch', 'pytorch', 'torchvision', # 310 MB
'tensorflow', 'tf', # 几百 MB
]2.3.6 collect_all() 自动收集
作用:自动收集某个包的所有内容。
from PyInstaller.utils.hooks import collect_all
# 收集 RapidOCR 的所有内容
tmp_ret = collect_all('rapidocr_onnxruntime')
datas += tmp_ret[0] # 数据文件
binaries += tmp_ret[1] # 二进制文件
hiddenimports += tmp_ret[2] # 隐藏导入
print(f"收集了 {len(tmp_ret[0])} 个数据文件")2.4 PYZ 阶段
2.4.1 基本用法
pyz = PYZ(
a.pure, # 纯 Python 模块
a.zipped_data, # 要压缩的数据
cipher=None, # 加密密钥(可选)
)2.4.2 参数说明
| 参数 | 说明 | 常用值 |
|---|---|---|
a.pure | Analysis 收集的纯 Python 模块 | 固定写法 |
a.zipped_data | 需要压缩的数据 | 固定写法 |
cipher | 加密密钥 | None(不加密) |
2.5 EXE 阶段
2.5.1 基础参数
exe = EXE(
pyz, # PYZ 对象
a.scripts, # 启动脚本
[], # 额外的脚本(一般为空)
exclude_binaries=True, # 是否排除二进制文件
name='MEMEFinder', # EXE 名称
debug=False, # 调试模式
bootloader_ignore_signals=False,
strip=False, # 是否去除符号表(Linux/Mac)
upx=True, # 是否使用 UPX 压缩
console=False, # 是否显示控制台
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None, # 目标架构
codesign_identity=None, # macOS 代码签名
entitlements_file=None, # macOS 权限文件
icon='assets/icon.ico', # 图标文件
)2.5.2 参数详细介绍
console - 是否显示控制台
console=False # GUI 程序,不显示黑窗口
console=True # 命令行程序,显示黑窗口icon - 程序图标
icon='assets/icon.ico'要求:
- 必须是
.ico格式 - 推荐尺寸:256x256 或 512x512
- 可以包含多个尺寸(Windows 会自动选择)
upx - 压缩 EXE
upx=True # 启用 UPX 压缩(推荐)
upx=False # 不压缩UPX 是什么?
- UPX = Ultimate Packer for eXecutables
- 压缩 EXE 和 DLL 文件
- 运行时自动解压(透明)
exclude_binaries - 是否排除二进制文件
exclude_binaries=True # 二进制文件放 _internal/ (推荐)
exclude_binaries=False # 全部打包进 EXE (不推荐)对比:
exclude_binaries=True | exclude_binaries=False |
|---|---|
| EXE 文件小 | EXE 文件巨大 |
DLL 放 _internal/ 目录 | 全部打包进 EXE |
| 启动稍快 | 启动极慢 |
| 文件夹模式 | 单文件模式 |
2.6 COLLECT 阶段
2.6.1 基本用法
coll = COLLECT(
exe, # EXE 对象
a.binaries, # 二进制文件
a.zipfiles, # ZIP 文件
a.datas, # 数据文件
strip=False, # 是否去除符号表
upx=True, # 是否 UPX 压缩
upx_exclude=[], # 排除 UPX 压缩的文件
name='MEMEFinder', # 输出文件夹名
)2.6.2 参数说明
| 参数 | 说明 |
|---|---|
exe | EXE 对象(从上一步来) |
a.binaries | 所有 DLL 等二进制文件 |
a.zipfiles | ZIP 压缩文件 |
a.datas | 所有数据文件 |
name | 输出文件夹的名称 |
upx | 是否压缩 DLL |
upx_exclude | 哪些文件不要压缩 |
2.6.3 upx_exclude - 排除某些文件的压缩
有些 DLL 不能用 UPX 压缩,否则会出错:
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
upx=True,
upx_exclude=[
'vcruntime140.dll', # VC++ 运行时
'python311.dll', # Python 解释器
],
name='MEMEFinder',
)3. 总结
Pyinstaller 流程图:
入口文件 (main.py)
↓
[Analysis]
分析依赖关系
↓
收集各种文件
├─ Python 模块
├─ 数据文件 (datas)
├─ 二进制文件 (binaries)
└─ 隐藏导入 (hiddenimports)
↓
[PYZ]
压缩 Python 代码
↓
[EXE]
生成可执行文件
↓
[COLLECT]
组装成文件夹
↓
最终成品
releases/
└── MEMEFinder/
├── MEMEFinder.exe
├── _internal/
└── 其他文件