简介:压缩包提供 PyInstaller 3.5 版完整源码与安装内容,面向需要将 Python 程序打包成独立可执行文件的开发者,解决目标机器未安装 Python 时的分发部署问题,也适合离线环境或固定版本构建场景。包内共 1307 个文件,其中 Python 源码 873 个、C 源码 20 个,另有 spec 构建配置、可执行文件、动态库、说明文档、图标与测试数据,压缩包整体仅 3.93MB,结构清晰,同时保留测试样例与多平台构建文件,便于对照实验。已有 2229 人学习下载,适合 PyInstaller 使用者、打包流程定制者以及想研究其内部机制的工程师。资源完整保留了核心启动与解压相关的 C 源码、命令行手册、跨平台构建脚本和 UI 资源,方便离线安装或二次开发;通过阅读这些文件,还能掌握依赖收集、资源嵌入和引导程序生成等关键机制。 我第一次拿到pyinstaller-pyinstaller-v3.5.zip这个压缩包时,说实话是有点懵的。文件名一看就是 GitHub 仓库的自动打包产物,但里面装的是什么、怎么装、装完又能干什么,我当时完全没有概念。后来在多个项目里反复用 PyInstaller 把 Python 脚本变成 Windows 下的 exe,再回头看这个 zip,才明白它其实是很多人接触 Python 打包工具时的第一个入口。尤其是当你在内网环境、离线环境,或者只是想通过源码包手动安装时,这类 zip 就派上用场了。
这篇文章不是官方文档的复述,而是把我自己下载、安装、打包、踩坑的完整过程梳理出来。从 zip 包解压后的目录结构,到命令行安装遇到的 PATH 问题,再到真正打包 exe 时的各种报错和应对方案,尽量一次讲透。无论你是刚接触 PyInstaller 的新手,还是已经打包过几个脚本但总在某些问题上卡住的老手,应该都能从这里找到有用的东西。
1. 拿到 zip 包之后:先分清三种安装方式的差别
pyinstaller-pyinstaller-v3.5.zip这种命名方式,是 GitHub 仓库的 Source code 打包格式。解压后会得到一个完整的源码目录,里面有 PyInstaller 主包、bootloader、setup.py、doc、tests 等。这个 zip 和直接用 pip 安装的 pyinstaller 之间的关系,可以从三个层面来看。
| 安装方式 | 获取位置 | 适合场景 | 常见问题 |
|---|---|---|---|
pip install pyinstaller | PyPI | 联网环境、快速上手 | 下载慢、超时 |
| GitHub zip 手动安装 | GitHub 源码包 | 离线内网、想读源码 | 依赖缺失、入口脚本缺失 |
| whl 文件离线安装 | PyPI Release | 离线环境、避免源码编译 | 必须匹配 Python 版本和平台 |
如果你只是想打包自己的脚本,直接 pip 是最省事的选择。但如果你拿到手的就是这个 zip,或者需要在隔离网络里安装,那么手动安装的路径必须搞清楚。接下来我按 zip 场景详细说。
1.1 zip 解压后的结构里藏着什么
解压之后,你会看到这样的目录结构:
pyinstaller-pyinstaller-v3.5/ ├── PyInstaller/ ├── bootloader/ ├── doc/ ├── tests/ ├── setup.py └── ...bootloader这个目录是 PyInstaller 生成 exe 时最核心的组件。它会根据当前的 Python 版本和操作系统,编译生成一个对应的启动加载器,这个加载器负责在目标机器上将打包好的 Python 程序和依赖释放并运行起来。zip 包里的 bootloader 源码是跨平台的,安装过程中会根据你当前解释器自动选择或编译合适的版本。
这里有一个很容易踩的坑:如果你的电脑同时装了多个版本的 Python,比如 Python 3.6 和 Python 3.8,那么你用哪个 Python 去执行安装脚本,生成的 bootloader 就绑定哪个解释器。如果后面换了另一个 Python 来跑打包命令,常会冒出和 bootloader 相关的报错,或者提示找不到对应平台的动态库。所以安装前先确认好你打算用哪个 Python 环境,后续打包全程都用它。
1.2 离线手动安装的正确打开方式
如果 zip 包里的源码版本所依赖的第三方库(比如 altgraph、pefile 等)没有提前装好,直接跑python setup.py install会暴露各种依赖缺失错误。我更推荐这种打开方式:
unzip pyinstaller-pyinstaller-v3.5.zip cd pyinstaller-pyinstaller-v3.5 pip install .用pip install .而不是python setup.py install,原因很简单:pip 会自动解析setup.py里声明的依赖,把它们安装到当前环境的 site-packages,同时在 Scripts 目录下生成pyinstaller.exe入口文件。如果你坚持用 setup.py 直接安装,依赖可能不完整,后续打包时更容易出现隐性问题。
如果是完全离线的机器,更稳妥的做法是在联网机器上用 pip 先把安装包拉下来:
pip download pyinstaller==3.5这会下载 PyInstaller 本身以及所有依赖的 whl 文件。把它们一起拷贝到目标机器,然后用:
pip install --no-index --find-links=./pyinstaller_packages pyinstaller这种方式比用 GitHub zip 更干净,因为 whl 是构建好的产物,不需要在目标机器上编译源码中的扩展模块。
1.3 为什么命令行总提示"不是内部或外部命令"
安装完成之后,最常见的一个问题是在 cmd 里输入pyinstaller,系统直接告诉你找不到这个命令。原因通常不是安装失败,而是 Python 的 Scripts 目录没有加进 PATH 环境变量。
Windows 上 pip 安装的可执行文件默认放在类似这样的路径下:
C:\Users\你的用户名\AppData\Local\Programs\Python\Python3x\Scripts当然如果你用的是虚拟环境,那就是venv\Scripts。有两个解决办法:一个是修改系统环境变量,把 Scripts 目录加进去;另一个是直接用模块方式调用:
python -m PyInstaller --version我个人的习惯是先跑这条命令做验证,如果能输出版本号,说明安装本身没问题,剩下的只是调用方式的问题。很多教程直接让你敲pyinstaller,一旦失败就引导你重装,结果绕了一大圈。
2. 打包第一个 exe:先理解 PyInstaller 在背后干了什么
很多人并不清楚执行pyinstaller -F xxx.py之后,工具具体做了哪些事情。理解这几个环节,对你排查各种报错非常有帮助。
PyInstaller 的完整流程可以拆成几步:
- 分析入口脚本,找出所有 import 的模块,递归生成依赖树。
- 收集这些模块对应的字节码、动态库(比如 .pyd、.dll)和扩展文件。
- 根据参数把数据文件、资源文件一并纳入。
- 将所有这些打包进一个 CArchive 数据块,并拼上匹配当前操作系统和 Python 版本的 bootloader。
- 输出到 dist 目录,生成最终的 exe 文件。
理解了这些,你就会明白为什么很多报错是"打包时找不到模块",而另一些是"运行时找不到文件"——这两类问题的排查方向完全不同。
2.1 最小复现:把 hello.py 变成 exe
先看最经典的场景。随便准备一个hello.py:
print("Hello, PyInstaller!")在命令行进入脚本所在目录,运行:
pyinstaller -F hello.py等待进度条走完,目录结构会变成:
hello.py build/ dist/ hello.exe hello.spec打开dist\hello.exe,窗口一闪而过,你甚至看不清输出内容。这时可以在 cmd 里直接运行dist\hello.exe,输出就能停留在终端窗口里。这个小细节很多人第一次都会困惑。
2.2 -F 到底要不要加:单文件模式和目录模式之争
-F表示 onefile,打包完成后只有一个 exe。好处是分发方便,发给别人一个文件就行。坏处是运行时需要先把内部资源解压到临时目录,启动速度会慢一些,而且这种自解压行为容易被杀毒软件标记为可疑操作。
-D是目录模式,也是默认模式,生成一个包含 exe 和一整套依赖文件的文件夹。首次启动速度快,调试也方便——因为程序加载了哪些文件一目了然,出问题更容易定位。
我的建议是:自己测试时用-D,正式分发给非技术用户时再用-F。如果你做的是绿色便携软件,-F确实省心,但前提是目标机器上没有拦截它启动的杀软。
| 需求 | 推荐参数 | 理由 |
|---|---|---|
| 发给同事临时用 | -D | 启动快,报错易定位 |
| 发布给终端客户 | -F | 只交付一个文件,使用门槛低 |
| 内部命令行工具 | -D | 便于维护和替换依赖 |
| 有 GUI 界面 | -F -w | 单文件且不弹出黑色控制台 |
2.3 资源文件和数据文件并不会自动打包
新手最容易犯的错误:脚本里通过相对路径读取config.json,开发时跑得好好的,打包后却报 FileNotFoundError。原因就是 PyInstaller 默认只收集 Python 模块和动态库,你手动 open 的资源文件不在自动收集范围内。
解决办法是使用--add-data参数。在 Windows 上:
pyinstaller -F --add-data "config.json;." main.py分号后面表示把 config.json 放到打包后的根目录。Linux 和 macOS 上分隔符要改成冒号。
更稳的方式是在代码里写一个通用的资源路径函数:
import sys import os def resource_path(relative): if hasattr(sys, "_MEIPASS"): return os.path.join(sys._MEIPASS, relative) return os.path.join(os.path.abspath("."), relative)sys._MEIPASS是 PyInstaller 在 onefile 模式下运行时设置的临时解压目录。加上这个函数后,不管开发环境还是打包后的环境,资源文件路径都能正确对得上。
3. 安装完 pyinstaller 却运行不了:我自己的排查顺序
如果已经到了"装完但一跑就报错"的环节,不要急着卸载重装,按下面的顺序过一遍,能定位到九成的问题。
3.1 先分清是"命令找不到"还是"模块导入失败"
在 cmd 里输入:
pyinstaller --version如果提示"不是内部或外部命令",先去检查 Scripts 目录是否在 PATH 里,或者直接改用模块方式调用。如果提示No module named 'PyInstaller',说明这个包没有装进你当前正在使用的这个 Python 解释器。
第二种情况在多 Python 共存的环境里特别常见。你明明 pip install 成功了,为什么还是找不到?因为你的pip和python可能并不属于同一套环境。在 cmd 里分别看一下:
python --version pip --version如果两者显示的路径不在同一个 Python 目录下,问题就出在这里。解决办法是用python -m pip install pyinstaller,强制把包安装到当前 python 对应的 site-packages 里。
3.2 安装过程卡住、超时或者提示校验失败
PyPI 的下载速度有时确实不稳定,pip 下载到一半就超时的情况我碰到过不少次。有两个方向可以解决。
一是换 pip 镜像源,在命令行里临时指定:
pip install pyinstaller -i https://pypi.org/simple二是直接下载 whl 文件离线安装。先在联网机器上用pip download pyinstaller==3.5把文件拉下来,然后拷贝到目标机器,用:
pip install pyinstaller-3.5-py2.py3-none-win_amd64.whl离线 whl 的好处是安装过程完全可复现,不会因为网络波动反复失败。
3.3 路径带空格、杀软拦截这些隐性因素
这是一个很低级但非常伤脑筋的坑。如果把解压后的 zip 放在一个带空格的目录下,比如D:\My Project\pyinstaller,某些依赖处理环节会出问题。尤其是涉及相对路径读取或者调用子进程时,空格可能会让路径解析出错。因此我建议把 PyInstaller 相关的项目目录统一放在无空格的路径下。
杀毒软件方面,Windows Defender 对刚生成的 exe 有时会临时拦截,表现为双击后没有任何反应。常见做法是把项目目录加入杀软白名单,或者暂时关闭实时防护做一次测试。注意我只是让你用于定位问题,不是鼓励长期关闭防护。
4. 真正打包时高频踩到的报错与对策
安装完成、能跑通第一个 hello.py 之后,真正的挑战才刚开始。以下是我在实际项目里反复遇到的几类问题。
4.1 动态导入让静态分析失效,运行时报缺模块
PyInstaller 是通过静态扫描 import 语句来收集依赖的,像下面这种写法它分析不出来:
mod_name = "utils.str_util" module = __import__(mod_name, fromlist=["*"])运行时走到这一句,Python 会报 ModuleNotFoundError,但打包阶段完全正常。这就是"打包成功、运行失败"的典型代表。
对策有三种:
- 用
--hidden-import显式指定:--hidden-import utils.str_util - 在代码里加一个不会执行但仍能触发静态分析的假导入:
import utils.str_util # 保留,不删除 - 把动态导入改成显式从模块字典加载。
我遇到过一个插件系统,模块名是由配置文件中字符串拼出来的,打包后运行时缺了一堆模块,最后就是用--hidden-import逐个补上的。如果你项目里也有类似的插件机制,建议提前扫描一下代码里所有__import__和importlib.import_module的调用。
4.2 RecursionError 和内存不足
当项目依赖的第三方库比较多时,PyInstaller 在分析阶段可能报 RecursionError。这通常是某个库自身递归实现得太深,导致默认递归上限被触发。常见于一些代码生成类的库和部分 ORM。
解决方案是修改 spec 文件。在执行过一次打包后,目录下会生成main.spec。打开它,在文件开头增加:
import sys sys.setrecursionlimit(5000)然后在 Analysis 部分确认入口脚本配置无误。修改完成后,不要再用pyinstaller main.py,而是执行:
pyinstaller main.spec这样 spec 里的配置才会生效。同理,如果我需要在打包阶段排除某些用不到的模块,也可以用 spec 里的excludes参数,这比命令行参数更灵活。
4.3 换台机器跑 exe 报错"Failed to load Python DLL"
这个问题多发生在用较新版本 Python 打包,但目标机器上没有安装对应版本的 VC++ 运行库时。Windows 上的 Python 从 3.5 开始依赖 Universal C Runtime,如果你的 exe 要在一台裸系统上运行,建议提前把 VC++ Redistributable 装一遍,或者确认目标机器的系统补丁已经更新到支持该运行库的版本。
遇到这类问题,我会先在目标环境里跑一下python --version,确认基础解释器环境一致,再逐步排查其他依赖。很多时候问题并不在 PyInstaller 本身,而在目标机器的系统环境。
4.4 杀毒误报如何缓解
PyInstaller 打出的 exe 经常被误报,本质上是因为它把 Python 解释器、程序代码和依赖打包成一个可自解压的可执行文件。杀毒软件看到"自我解压并释放文件"的行为模式,加上文件没有官方数字签名,就容易产生告警。
应对思路有几种:
- 给 exe 做代码签名,签名后误报率会明显下降。
- 把 onefile 模式改成 onedir 模式,减少"启动时自解压"这个敏感动作。
- 在内网环境里,把程序目录加入杀软排除项。
需要说明的是,误报不等于真的病毒。如果你的代码来源可靠,那么 PyInstaller 生成的包只是因为行为特征相似被放在了可疑名单里。
5. 还在用 v3.5?不妨看看这些升级判断标准
如果你手里这个pyinstaller-pyinstaller-v3.5.zip来自某个老项目,或者目标环境锁死在老版本 Python 上,那继续用老版本并不是什么问题。但如果你的环境允许升级,我建议认真考虑换个更新的版本。
5.1 v3.5 和当前版本的主要差异
PyInstaller 3.5 对 Python 版本的支持范围很有限,大致对应 Python 2.7 和 3.3 到 3.6 这个区间。如果你现在用的是 Python 3.8 甚至更高,v3.5 很有可能装都装不上,更别提打包。新版 PyInstaller 的 bootloader 机制更成熟,对 Windows 上 DLL 的依赖分析也更全,打包生成的程序在目标机器上的崩溃率明显更低。
如果你的目标环境是 Python 3.10 以上,那就不要折腾这个 zip 了,直接去 PyPI 装新版本即可。zip 源码包只在特定历史条件下才有价值。
5.2 什么情况下建议留在老版本
一种情况是你的旧项目虚拟环境里跑的还是 Python 2.7 或 Python 3.5,为了保持打包行为一致,不想引入新版本带来的潜在变化。另一种是长期断网的内网环境,手里只有这个 zip 包,那手动安装的链路就要十分熟练。
我的建议很明确:新项目一律用新版本;老项目如果打包后能正常运行,就别动;手里只有 zip 包且不能联网时,用pip install .方式装,不要直接python setup.py install。
最后说一个我自己的操作习惯:每次拿到 PyInstaller 的 zip 包,我都会先在项目根目录建一个临时文件夹专门存放 build 和中间产物,所有输出都指向那里。这样就算打包出问题,也不会污染原项目结构。毕竟打包这件事,脏活累活都让机器去干,自己的项目目录保持干净,后面维护起来才不闹心。
本文还有配套的精品资源,点击获取