下面我将从 原因分析、通用优化方法、高级技巧 和 替代方案 四个方面,为你提供一套完整的解决方案。

问题根源分析:为什么 PyInstaller 打包的文件这么大?
我们要明白 PyInstaller 默认做了什么:
- 打包整个 Python 环境:PyInstaller 默认会分析你的代码,找到所有导入的模块,然后将这些模块连同 Python 解释器一起打包进去,如果你的项目依赖了很多第三方库(如
numpy,pandas,torch),体积自然会非常庞大。 - 包含不必要的文件:默认配置下,PyInstaller 可能会打包一些你用不到的动态链接库(
.dll,.so)或数据文件。 - 未压缩或压缩率低:打包后的可执行文件本身就是一个压缩包,如果配置不当,压缩效果会很差。
通用优化方法(90% 的情况适用)
这些方法简单有效,应该首先尝试。
使用 --onefile 还是 --onedir?
这是一个关键选择。
--onedir(默认): 生成一个文件夹,包含可执行文件和所有依赖的.dll和.pyd文件。- 优点: 启动速度快,因为解压步骤少;更新单个依赖时无需重新打包整个程序。
- 缺点: 需要分发一个文件夹,而不是单个文件。
--onefile: 生成一个单独的可执行文件,首次运行时,它会将自身解压到一个临时目录,然后执行。- 优点: 分发方便,只有一个文件。
- 缺点: 启动速度稍慢(因为要解压);解压后的临时文件会留在用户电脑上(可以通过
--noupx或清理代码处理)。
建议: 如果不追求极致的单文件分发,优先使用 --onedir,它的启动速度和性能优势明显,而且对于很多应用来说,一个文件夹也是可以接受的。

使用 --clean 和 --noconfirm
--clean: 在每次打包前,先清理旧的build和dist目录,这能确保没有旧文件残留,导致打包结果不准确。--noconfirm: 覆盖输出目录而不提示。
最佳实践: 在每次打包时都加上这两个参数。
pyinstaller --clean --noconfirm your_script.py
排除不必要的模块和数据 (--exclude-module 和 --add-data)
这是最有效的瘦身方法之一。
-
排除模块: 如果你使用了某个库,但你的代码并没有用到它的全部功能,可以尝试排除。
matplotlib会打包很多后端,如果你只用Agg(无界面渲染),可以排除其他后端。tkinter的tcl库非常巨大,如果不用 GUI,可以尝试排除。
# 排除 matplotlib 的 Qt 和 Tk 后端 pyinstaller --exclude-module matplotlib.backends.qt4 \ --exclude-module matplotlib.backends.qt5 \ --exclude-module matplotlib.backends.tkagg \ your_script.py -
添加数据文件: 如果你的程序需要读取图片、配置文件等,需要使用
--add-data,格式为源路径:目标路径,路径用分号 (Windows) 或冒号 (Linux/macOS) 分隔。# 假设你的图片在 res/ 目录下,打包后希望存放在程序内的 res/ 目录 pyinstaller --add-data "res;res" your_script.py
使用 UPX 压缩 (--upx-dir 或 --noupx)
UPX 是一个可执行文件压缩工具,PyInstaller 可以用它来压缩最终的可执行文件和依赖库。
- 安装 UPX: UPX 官网 下载并解压,将
upx.exe(Windows) 或upx(Linux/macOS) 所在目录添加到系统PATH中。 - 启用 UPX:
pyinstaller --upx-dir "C:\path\to\upx" your_script.py
- 禁用 UPX: 有时 UPX 会和某些杀毒软件冲突,或者导致启动失败,可以禁用它。
pyinstaller --noupx your_script.py
高级优化技巧(针对顽固问题)
如果通用方法效果不佳,可以尝试以下进阶技巧。
分析依赖 (--analyze 和 --log-level=INFO)
PyInstaller 2.1+ 版本内置了依赖分析器,可以更准确地找出真正需要的模块。
--analyze: 生成一个.features文件,详细列出了每个模块和它被引用的原因,可以帮助你找到不必要的依赖。--log-level=INFO: 在控制台输出更详细的信息,可以看到哪些模块被包含或排除了。
pyinstaller --onefile --clean --noconfirm --log-level=INFO --analyze your_script.py
打包后,在 build/your_script/ 目录下会找到 your_script.spec 和 your_script.analyze.json,里面包含详细的依赖分析。
手动编辑 .spec 文件
.spec 文件是 PyInstaller 的配置文件,通过修改它可以实现更精细的控制。
-
hiddenimports: PyInstaller 没能自动检测到某些动态导入的模块,可以手动添加。# your_script.spec a = Analysis( ['your_script.py'], ... hiddenimports=['some_module_that_was_not_detected'], ... ) -
excludes: 这是最强大的排错工具,将你确定用不到的模块名加入列表。# your_script.spec a = Analysis( ['your_script.py'], ... excludes=['matplotlib', 'PIL', 'tkinter'], # 强制排除这些库 ... )你可以通过
pip list查看所有已安装的库,然后逐一尝试排除,观察打包体积的变化。 -
binaries和datas: 手动添加或移除二进制文件和数据文件。# your_script.spec a = Analysis( ... binaries=[('path/to/your/custom.dll', '.')], # 添加一个特定的dll datas=[('config.json', '.')], # 添加数据文件 ... )
使用虚拟环境
这是最重要、最基础的一步!
永远不要在你开发的全局 Python 环境中打包,创建一个干净的虚拟环境,只安装你的项目真正需要的依赖。
# 1. 创建虚拟环境 python -m venv myenv # 2. 激活虚拟环境 # Windows: myenv\Scripts\activate # Linux/macOS: source myenv/bin/activate # 3. 只安装项目依赖,不要装 jupyter notebook, ipython 等开发工具 pip install -r requirements.txt # 4. 在这个干净的环境中运行 PyInstaller pyinstaller --onefile your_script.py
这样做可以避免把开发工具、测试框架等打包进去,能极大减小体积。
针对 numpy, pandas, scipy 等科学计算库的优化
这些库体积巨大,因为它们包含了大量的编译代码(C/C++/Fortran)。
- 使用
--exclude-module: 尝试排除你不用的子模块。# 如果只用numpy的数组功能,可以尝试排除其子模块 pyinstaller --exclude-module numpy.testing \ --exclude-module numpy.distutils \ --exclude-module numpy.f2py \ your_script.py - 从源码编译: 这是一个更复杂但更有效的方法,在虚拟环境中,使用
pip install --no-binary :all: numpy来从源码编译安装,这样 PyInstaller 只会打包你编译时用到的部分,但这需要你系统上有相应的编译工具链。
替代方案
PyInstaller 实在无法满足你的需求,可以考虑以下替代工具。
- Nuitka
- 特点: 它不是一个打包工具,而是一个 Python 编译器,它将你的 Python 代码(包括 C 扩展)编译成一个真正的本地可执行文件(C++ 程序)。
- 优点: 启动速度更快,运行性能更好,通常打包体积比 PyInstaller 更小、更干净。
- 缺点: 编译过程非常耗时,兼容性需要测试。
- 用法:
# 首先用 PyInstaller 生成
