简介PyInstaller依赖包.zip是一套面向Windows离线部署场景的Python打包工具资源包专为需要在内网或未预装Python的计算机上完成应用发布的技术人员设计。压缩包整体大小约6.21MB文件类型以Python模块及源码为主核心组件包括PyInstaller主程序、pywin32-ctypes、altgraph、future与pefile分别负责程序打包、Windows API调用、依赖关系分析、跨版本兼容以及PE文件解析彼此协同保证打包过程顺畅。资源已有877人学习。相较于在线安装该离线包屏蔽了网络波动与镜像失效风险支持快速搭建可重复的构建环境解压后即可安装配合组件功能说明读者既能上手完成exe生成也能深入理解PyInstaller梳理依赖、封装运行库的过程。对于不熟悉pip操作的用户或需要批量交付独立程序的场景此资源能有效降低部署门槛为日常开发与系统集成提供可靠支撑。1. 一个离线依赖包把PyInstaller打包从玄学变成确定性操作接手某项目时碰到的头一件事就是在内网机器上把Python脚本打包成exe。以为装个pyinstaller就完事结果pip在线拉了半小时依赖版本漂移、hook不匹配、装完还缺模块整个过程像个黑匣子。那份pyinstaller依赖包.zip本质上是把PyInstaller及其配套依赖锁死在一个压缩包里专门对付这种场景没有外网、需要重复打包、或者不想每次都被最新版依赖坑一遍。谁是目标用户内网部署的开发者、给别人交付exe的、还有被打包日志搞到怀疑人生的新手。它的价值不是省一次pip install是让打包结果可复现版本不再偷偷变。2. PyInstaller依赖包拆解四个核心组件与为什么必须锁版本2.1 依赖包里的四件套pywin32、pefile、altgraph、hooks-contrib解压开这份zip你大概会看到一堆wheel文件真正的核心是四件。pywin32负责Windows平台的地基PyInstaller生成的exe在Windows上运行要靠它去调用系统API、注册COM组件、处理进程句柄少了它连启动流程都走不通。pefile是用来读写PE文件结构的PyInstaller要把你的Python解释器、依赖库都塞进一个可执行文件里必然要改exe的头部信息、图标和版本资源pefile就是干这件事的工具。altgraph是一个图结构库PyInstaller靠它构建import依赖图把哪个模块引用了哪个模块梳理成一张完整的图打包时顺着图把所有东西收集起来。最后是pyinstaller-hooks-contrib这才是最容易踩坑的部分——它不是PyInstaller本体而是一大堆第三方库的hook打包规则集合。每个hook告诉PyInstaller这个库有一批隐藏导入文件、数据文件、二进制文件打包的时候别漏掉。这四件套的关系可以这么理解pywin32提供系统层能力pefile负责修改产物结构altgraph负责分析依赖关系hooks-contrib负责补全第三方库的特殊规则。打包过程中PyInstaller本体负责调度四件套缺一不可。常见错误是只装了PyInstaller主包就以为万事大吉结果打包时提示找不到模块恰恰是这些配套组件没到位。从文件结构上看zip里按wheel包名分文件文件名带着平台标签和Python版本标签比如pywin32-306-cp38-cp38-win_amd64.whl这种格式。看到这种命名就要警觉一件事wheel的Python版本必须和你目标机器上的解释器版本一致cp38的wheel装不进cp39的环境。这是离线安装的第一道门槛后文单独说。2.2 为什么不用在线pip安装版本漂移是打包失败的源头很多人会问既然PyInstaller能用pip直接装为什么还要费劲搞一个离线依赖包答案很简单——版本漂移。你在今天执行pip install pyinstaller拿到的是当天的最新版本。三个月后重新执行拿到的是三个月后的最新版本。而PyInstaller的hook规则跟Python版本、第三方库版本深度绑定新版本可能修复了老问题也可能引入新问题。某开发者的血泪经历是某次给一个用pandas和openpyxl的脚本打包第一次打包一切正常过了一个月在另一台机器上打包PyInstaller自动升级到了新版hook规则全变exe启动后直接报ModuleNotFoundError。排查了两个小时最后发现是pyinstaller-hooks-contrib新版本改了openpyxl的收集规则把数据文件漏掉了。这就是版本漂移的代价——同一个源码两次打包结果不一样而exe这种交付物最怕不确定性。离线依赖包的核心价值就是把这种漂移锁死。zip里每个wheel的版本都是固定的安装时--find-links指向本地目录pip不会去远程拉任何东西装什么版本全是确定性的。我一般会在zip里额外放一份requirements.txt把每个依赖的精确版本号列出来方便后续追溯。2.3 离线分发的两条路径wheel直装与目标机复制拿到zip之后怎么落地取决于你的场景。路径一在网络隔离或内网环境直接把zip传到目标机器解压后用pip install --no-index --find-links./解压目录离线安装。这种方式适合还没装过PyInstaller的干净环境。路径二如果有一台已经装好的机器可以把site-packages里PyInstaller相关的目录直接打包复制到另一台机器的相同Python版本环境里。这种方式更快但是风险高因为site-packages里可能有编译过的.pyd文件换机器或换Python版本容易翻车。我推荐还是走wheel直装这条路虽然多敲几条命令但可校验、可回滚。卸载时pip uninstall pyinstaller pywin32 pefile altgraph pyinstaller-hooks-contrib一键清理干净。复制目录的方式一旦坏了连卸载都是个问题。3. 把依赖包用起来离线安装到首次打包的全流程3.1 解压与校验先看wheel文件名里的Python版本标签拿到zip的第一步不是急着安装而是先解压、先看、先确认环境匹配。# 解压依赖包到工作目录 unzip pyinstaller依赖包.zip -d pyinstaller_deps # 查看解压结果重点看wheel文件名里的cp标记和平台标记 ls -la pyinstaller_deps/解压出的wheel目录里文件名的格式通常是包名-版本号-cpXY-cpXY-win_amd64.whl。cpXY就是Python版本cp38对应Python 3.8cp39对应Python 3.9win_amd64对应64位Windows。你在目标机上先跑一句python --version确认解释器版本然后对照wheel文件名版本对不上就别装装了也是白装pip会直接拒绝。同时检查一下zip里是否包含pyinstaller主包有的依赖包会精简到只有依赖项主包需要另配。标准做法是主包四件套一个requirements.txt都齐了才是完整的离线包。管我在哪台机器上用先把版本匹配这一关过了再说——这一步花不了两分钟能省下后面一小时的排错。3.2 离线安装命令与参数说明环境确认完毕后执行离线安装。注意命令里的两个关键参数别写错# 使用本地目录作为唯一依赖源禁止访问远程索引 pip install --no-index --find-links./pyinstaller_deps pyinstaller pywin32 pefile altgraph pyinstaller-hooks-contrib--no-index的意思是不要连PyPI远程索引哪怕你的机器能上网也强制不走远程。--find-links指定本地wheel目录pip只从这个目录里找匹配的包。这两个参数组合使用就把pip彻底锁在内网环境里。如果提示某个包已经安装而你想确保用的是zip里的版本加一个--force-reinstall --no-depspip install --no-index --find-links./pyinstaller_deps --force-reinstall --no-deps pyinstaller pywin32 pefile altgraph pyinstaller-hooks-contrib--no-deps在这里很重要因为离线环境下pip可能去解析一些不在本地目录里的传递依赖加了它pip就只装你指定的包不去理会依赖关系。既然zip已经把所有依赖都准备好了--no-deps是最稳妥的。装完验证一下版本确认安装的是zip里的定点版本而不是历史残留pyinstaller --version pip show pyinstaller pefile altgraph | grep -E Name|Version3.3 首次打包一个带第三方库的最小案例依赖装好就该跑通一次完整打包。我用一个同时依赖requests和tkinter的脚本来演示——这两个库一个涉及网络请求、一个涉及GUI都是容易在打包时出状况的代表。import requests import tkinter as tk def fetch_ip(): try: resp requests.get(https://api.ipify.org, timeout3) return resp.text except Exception as e: return ferror: {e} root tk.Tk() root.title(ip demo) label tk.Label(root, textclick to fetch ip) label.pack() def on_click(): label.config(textfetch_ip() or fetch failed) btn tk.Button(root, textfetch, commandon_click) btn.pack() root.mainloop()这个脚本最能暴露问题的地方在requests——它内部有大量动态导入和数据文件如果不做特殊处理PyInstaller的静态分析经常收集不全。打包命令如下pyinstaller --windowed --onefile --name ip_demo ip_demo.py--windowed表示这是一个GUI程序Windows下运行时不弹出黑色控制台窗口--onefile把所有依赖打包成单个exe文件--name指定产物名称默认是脚本名。执行完查看dist目录ip_demo.exe就在那里。3.4 验证打包成果依赖缺失检查方法打包成功不等于运行成功。执行产物如果GUI能弹出来、点击按钮能拿到IP那说明requests和tkinter都收集干净了。但更多时候第一次运行就会报错。最常用的验证方法是带着--log-level DEBUG重新打包一次把日志级别提到DEBUGPyInstaller会把每一步收集了哪些模块、哪些文件打印出来pyinstaller --windowed --onefile --name ip_demo --log-level DEBUG ip_demo.py看日志里有没有WARNING: Hidden import not found或ERROR: Module not found这类字眼有就是有漏网之鱼。另外--onefile模式下exe运行时会把所有临时文件解压到临时目录程序崩溃时信息一闪而过很难看清常见的做法是先在--onedir模式下跑一遍确认能运行再切成单文件。目录模式下所有依赖都在dist/ip_demo/里方便检查哪些DLL和文件被收集进来了。4. 核心参数怎么调从单文件到目录模式的三个关键取舍4.1 --onefile与--onedir的取舍启动速度与误报率的平衡--onefile把一切塞进一个exe交付时只给对方一个文件看起来很爽。代价是启动慢——每次运行都要把整个依赖解压到系统临时目录解压几百个文件的时间叠加起来程序启动延时可能从几百毫秒拉到好几秒。更麻烦的是--onefile的解压行为会触发安全软件敏感反应杀软经常把这种自解压结构判定为可疑行为。--onedir则生成一个目录exe旁边跟着一堆DLL和依赖文件启动速度明显更快安全软件误报率也低一些。缺点是交付时要打包整个目录对方拿到一堆文件会觉得混乱。我的经验是对外交付给小规模用户--onedir优先个人用、内部工具、文件数量敏感的场景再考虑--onefile。参数切换就在一行命令里但选型之后的影响是长期的尤其是杀软误报那条--onefile几乎躲不掉。4.2 --hidden-import与--collect-all动态导入的暗坑requests能跑通不代表所有库都能跑通。PyInstaller通过静态分析扫描import语句但很多库会动态拼接模块名导入比如__import__(util_ name)这种写法静态扫描根本识别不出来。另一个常见场景是importlib.import_module按配置文件的字段动态加载插件以及pkgutil.iter_modules遍历子模块。这些都是PyInstaller的盲区。踩坑实录某脚本用到了一组自己写的插件插件文件放在plugins/目录下主程序用importlib按注册表动态加载。打包后运行报ModuleNotFoundError原因就是PyInstaller分析主程序时根本不知道plugins/下还有模块。解决办法是显式指定pyinstaller --windowed --onedir --hidden-importplugins.plugin_a --hidden-importplugins.plugin_b --add-data plugins;plugins main.py--hidden-import让PyInstaller强制把指定模块收集进来--add-data把整个插件目录作为数据文件复制过去。注意Windows下--add-data的源路径和目标路径用分号分隔Linux下是冒号这个字符差异坑了很多人。如果库本身附带的数据文件、图标、配置文件特别多还可以用--collect-all 库名它等价于同时收集子模块、数据文件和二进制文件pyinstaller --collect-all pandas main.pypandas、numpy、scipy这类科学计算库强烈建议用--collect-all它们的编译模块和资源文件分布散手动--hidden-import漏网概率高。4.3 --spec文件把参数固化成可复现配置命令行参数每次都要敲一遍时间长了自己都容易忘。PyInstaller会把本次打包参数生成一个.spec文件路径在build/目录下。这个文件是Python语法里面定义了Analysis、PYZ、EXE和COLLECT的配置。后续再打包时直接复用spec文件保证参数完全一致pyinstaller --windowed --onedir main.specspec文件里hiddenimports是一个列表datas是一个二元组列表binaries用于额外二进制文件。把这些字段都维护好等于把打包逻辑从命令行搬进了版本管理后续靠git diff就能看到打包配置的变更历史。我习惯把spec作为打包配置的唯一真源命令行只保留极少常用参数。5. PyInstaller打包避坑与常见问题排查现象、原因、解决5.1 现象pip安装依赖时疑似卡住进度条长时间不动原因在线安装时网络不通或连接超时pip默认会重试几次每次重试都要等很久。另一个附带问题是本地缓存里有损坏的下载文件pip的缓存机制不会自动校验完整性会反复尝试用损坏的缓存安装。解决先停掉当前安装清理pip缓存再用离线方式从zip安装。# 停止安装后清理缓存 pip cache purge # 安装时禁用缓存直接从本地目录读取 pip install --no-index --find-links./pyinstaller_deps --no-cache-dir pyinstaller pywin32 pefile altgraph pyinstaller-hooks-contrib--no-cache-dir告诉pip别写缓存也别读缓存强制从--find-links指定的目录读取。做完这步卡死概率大幅下降。如果还是卡检查archives目录里的wheel文件大小是否为0或异常偏小零字节的wheel文件就是上次下载中断留下的残骸。5.2 现象离线安装时报错Wheel format not supported原因zip里的wheel文件名带的是cp39你的环境是Python 3.12或者wheel是win32标签你的Python是64位。pip对wheel的兼容性检查非常严格标签不完全匹配就拒绝安装。解决思路不是强装而是找到与你环境匹配的wheel包。具体操作先用python -V拿到精确的Python小版本再对照wheel文件名里的cpXX标签。线上搜索或找配对的离线依赖包时把版本标签看清再要。如果手头只有不匹配的包两个变通方案一是装一个对应版本的Python环境二是用pip的--platform参数尝试强制安装但不保证运行时不出问题不推荐。5.3 现象打包成功exe运行提示ModuleNotFoundError原因这是PyInstaller最常见的问题动态导入模块没被静态分析捕获。代码里用了importlib、__import__、或按字符串拼接方式导入的PyInstaller统统看不到。还有一种情况是数据文件缺失比如读取config.yaml、icon.png这种非Python文件它们默认不会被打包进exe。解决先看运行时完整报错确认缺的是模块还是数据文件。缺模块就加--hidden-import缺数据文件就加--add-data。如果用的是pandas、openpyxl这类大型库直接--collect-all一键收敛。预防办法是打包前跑一遍pyinstaller --log-level DEBUG日志里把所有检测到的隐藏导入列出来有警告就先处理完再打包。5.4 现象exe被杀毒软件隔离或报警原因PyInstaller生成的exe有几处特征容易触发安全软件的启发式规则--onefile的临时解压行为是重灾区。其次未签名的可执行文件在安装了企业安全策略的机器上运行时会直接拦截。解决优先改用--onedir降低误报率。给exe进行代码签名可以明显提升信任度但需要有效的签名证书。如果这两步都做不到只能在安全软件里把exe加入白名单但这不是长久之计。提到你是不是在躲杀软的边界问题——其实不是是杀软对PyInstaller的检测规则太敏感代码签名才是正规出路。6. 把打包流程固定成脚本参数校验与回归验证技巧6.1 用requirements.txt把zip内的版本固定下来解压后的zip里各wheel版本是固定的但手动记录版本容易漏直接在解压目录里生成一个锁定文件pip freeze | findstr /i pyinstaller pywin32 pefile altgraph hooks-contrib locked_requirements.txtpip freeze输出当前环境所有包及精确版本号findstr筛选出PyInstaller相关的四个包重定向到文件。下次无论在哪台机器上装先读这个文件确认版本和预期一致再继续。6.2 打包前校验脚本版本、兼容性、依赖完整性我习惯在每次打包前跑一段小脚本把环境里的关键变量一次查清防止在错误的环境里白白打半小时包。import sys import importlib.metadata as md REQUIRED { pyinstaller: 6.0, pywin32: 300, pefile: 2023, altgraph: 0.17, } for pkg, version_req in REQUIRED.items(): try: version md.version(pkg) print(f[OK] {pkg} {version}) except md.PackageNotFoundError: print(f[ERROR] {pkg} not installed. exit.) print(f[INFO] Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro})这段脚本用importlib.metadata读取已安装包的版本号再与预期范围比对。如果哪个包缺失或版本不对直接打[ERROR]终止。脚本里的版本范围是我根据zip里wheel的版本来写的你可以按自己zip里的实际版本改。打包前执行它2秒判断环境是否靠谱省去盲目打包。6.3 用spec文件做回归对比spec文件是配置快照打包环境变了、参数调整了spec文件都会留下痕迹。我维护spec文件的方式是把它纳入版本管理目录每次打包前检查变更git diff build/main.spec看diff里hiddenimports列表和datas列表的变化比看打包日志直观得多。打包产物的回归验证我会对比两次打包输出的目录哈希# 打包后对dist目录生成校验 find dist -type f -exec md5sum {} \; | sort -k 2 build_hash.txt把这份build_hash.txt保存下来下次打包后重新生成并对比如果哈希有差异说明产物变了需要追查是源码变更还是依赖变更导致的。这个习惯帮我抓过一次pyinstaller-hooks-contrib悄悄升级后hook行为变化的问题——哈希对比暴露出exe大小异常追查才发现版本漂移。从那以后我每次打包都强制走一遍校验脚本加spec对比先确认环境版本没变、参数没变才动手打包。这个习惯坚持下来打包这件事从玄学慢慢变成了可预期的操作同类问题反复出现的情况少了大半。希望帮到你。本文还有配套的精品资源点击获取