PyInstaller依赖打包失败?用可复现依赖快照解决模块缺失问题
简介本资源是PyInstaller在Windows平台下的离线安装包专为无网络环境、受限IT权限或Python基础薄弱的开发者设计解决无法通过pip在线安装PyInstaller及其依赖时的打包部署难题。压缩包为ZIP格式大小6.21MB内含PyInstaller核心源码pyinstaller-pyinstaller-3e6f7dc、Windows系统适配组件pywin32-ctypes-0.2.0、依赖分析工具altgraph-0.16.1、PE文件处理库pefile-2019.4.18及跨版本兼容支持模块future-0.18.1覆盖从进程创建、注册表操作到可执行文件结构修改的完整打包链路。目前已有877人学习下载适合需快速在隔离机房、教学机房或客户现场部署Python应用的工程师与运维人员。用户可直接解压后按步骤安装无需额外配置Python环境即刻获得开箱可用的PyInstaller离线打包能力并同步掌握各依赖库在打包流程中的实际作用与协同逻辑。1. PyInstaller 依赖包打包失败别急着重装Python先看这版「开箱即用」的依赖快照你是不是也遇到过本地写好的脚本用pyinstaller main.py一打包生成的 exe 在同事电脑上双击就闪退或者在 CI 环境里反复报ModuleNotFoundError: No module named xxx查了半天发现是某个子依赖比如certifi的路径被硬编码了、pywin32的 DLL 没自动拷进去、matplotlib的 backend 配置被忽略——根本不是代码问题而是 PyInstaller 对当前环境的「理解偏差」。这不是玄学是它在扫描时对site-packages的遍历逻辑、对__import__动态调用的静态分析盲区、以及对.pth文件和命名空间包的处理惯性共同导致的。这份pyinstaller依赖包.zip不是工具而是一份可复现、可回滚、带版本锚点的依赖快照它包含经实测能通过--onefile--console完整构建并运行的最小依赖集合含pyinstaller本身 setuptoolswheelpip锁定版本 常见 hook 补丁专为 Python 3.8–3.11 主流版本设计。适合正在做自动化部署、需要交付稳定二进制包、或被--hidden-import和--add-data参数绕晕的新手也适合熟手在换新机器/重装系统后5 分钟内拉起一套「已验证能跑通」的打包基线——毕竟比起花两小时 debug 一个ImportError直接解压、激活、打包才是真正的后悔药。2. 下载与环境准备确认 Python 版本、解压路径、权限三要素2.1 确认你的 Python 解释器版本与架构必须匹配PyInstaller 打包生成的可执行文件其运行时依赖与打包时所用 Python 解释器的版本、位数32/64bit、编译器链MSVC vs MinGW强绑定。若你在 Python 3.9.764bitMSVC 14.2下打包却试图在 Python 3.10.164bit但用的是 MinGW 编译环境下运行生成的dist/main.exe大概率触发VCRUNTIME140.dll找不到或api-ms-win-crt-runtime-l1-1-0.dll缺失。因此第一步不是下载 zip而是执行python --version python -c import platform; print(platform.architecture()); print(platform.machine())输出应类似Python 3.9.13 (64bit, WindowsPE) AMD64提示pyinstaller依赖包.zip中预置的pyinstaller-5.13.0-py39-none-win_amd64.whl仅适配 Python 3.9.x Windows 64bit。若你用的是 Python 3.10 或 3.11请勿强行安装该 wheel——它会因 ABI 不兼容直接报ERROR: pyinstaller-5.13.0-py39-none-win_amd64.whl is not a supported wheel on this platform。此时请跳转至 2.3 节手动下载对应版本。2.2 解压到无空格、无中文、非系统路径的纯英文目录PyInstaller 内部大量使用subprocess.Popen调用cmd.exe执行临时命令路径中若含空格如C:\Users\My Name\Downloads或中文如D:\项目\打包工具会导致FileNotFoundError: [WinError 2] 系统找不到指定的文件。这不是 bug是 Windows CMD 的历史遗留限制。正确做法是# 推荐解压到根目录下的短路径 mkdir C:\pyinst-baseline cd C:\pyinst-baseline # 将 pyinstaller依赖包.zip 解压至此目录确保解压后内部结构为./pyinstaller-5.13.0/ ./requirements-lock.txt ./hooks/解压后目录结构必须严格如下共 4 个一级子项C:\pyinst-baseline\ ├── pyinstaller-5.13.0\ # PyInstaller 源码目录含 ./PyInstaller/ 子包 ├── requirements-lock.txt # pip freeze 的锁定列表含版本号与哈希 ├── hooks\ # 自定义 hook 目录含 hook-matplotlib.py, hook-pywin32.py 等 └── build-tools\ # 辅助脚本build.bat, verify-env.ps1注意不要将 zip 直接解压到C:\Users\...或D:\Program Files\下也不要保留 zip 同名文件夹如pyinstaller依赖包\必须解压出内容到目标空目录。2.3 根据 Python 版本选择并安装对应 PyInstaller wheelpyinstaller依赖包.zip中提供的 wheel 是经过编译优化的发行版比pip install pyinstaller更稳定避免了本地编译pywin32的失败风险。但 wheel 名称已编码 Python 版本标签py39表示仅支持 Python 3.9。确认版本后执行# 若 python --version 输出为 Python 3.9.x pip install C:\pyinst-baseline\pyinstaller-5.13.0-py39-none-win_amd64.whl # 若为 Python 3.10.x需手动下载官方 PyPI 提供全版本 pip install pyinstaller5.13.0 --only-binarypyinstaller # 若为 Python 3.11.x注意5.13.0 官方 wheel 未正式支持 3.11需降级或升版 pip install pyinstaller6.0.0 --only-binarypyinstaller--only-binarypyinstaller参数强制 pip 跳过源码编译只从 PyPI 下载预编译 wheel。这是关键——很多翻车源于 pip 试图用你的本地 MSVC 编译pywin32而你的 VS Build Tools 可能缺失Windows 10 SDK组件。2.4 验证基础安装检查 PyInstaller 是否识别到 Python 环境安装完成后不急着打包先运行诊断命令pyinstaller --version pyinstaller --pathsC:\pyinst-baseline\pyinstaller-5.13.0 --debugall --onefile --console test.py其中test.py是一个极简文件# test.py import sys print(Python version:, sys.version) print(Executable:, sys.executable) input(Press Enter to exit...)重点观察输出中的INFO: PyInstaller: 5.13.0和INFO: Python: 3.9.13是否一致。若出现WARNING: Unable to find dependency python39.dll说明 PyInstaller 未能定位到你的 Python 安装目录——此时需手动设置--paths指向 Python 的DLLs和Lib目录pyinstaller --pathsC:\Python39;C:\Python39\DLLs;C:\Python39\Lib --onefile test.py3. 依赖管理用 requirements-lock.txt 锁定第三方包版本3.1 requirements-lock.txt 的作用远超 pip freezepyinstaller依赖包.zip中的requirements-lock.txt不是简单pip freeze reqs.txt的产物而是通过pip-compile来自pip-tools生成的可重现依赖树。它包含每个包的精确版本如requests2.28.1包的 SHA256 哈希值--hashsha256:...确保下载内容未被篡改依赖传递关系如urllib31.21.1,1.27是requests的子依赖这意味着即使requests发布了 2.28.2你的环境仍会强制安装 2.28.1避免因上游小版本更新引入的ImportError例如某次 requests 升级后移除了packages.urllib3.util.retry中的某个类。3.2 使用 pip-sync 一键同步环境推荐比 pip install -r 更安全pip-sync是pip-tools的核心命令它会删除当前环境中所有不在requirements-lock.txt中的包再安装列表中指定的版本。这比pip install -r requirements-lock.txt更彻底能清理掉开发过程中误装的干扰包。# 1. 先安装 pip-tools它本身不在 lock 文件中需单独装 pip install pip-tools # 2. 进入解压目录执行同步假设 lock 文件在 C:\pyinst-baseline\ cd C:\pyinst-baseline pip-sync requirements-lock.txt # 3. 验证结果只应看到 lock 文件中列出的包 pip list --outdated # 应无输出所有包均锁定提示pip-sync会卸载pip、setuptools、wheel以外的所有包。若你本地有其他项目依赖务必在虚拟环境中操作见 3.3。3.3 强烈建议在独立虚拟环境中应用此依赖快照直接在系统 Python 环境中运行pip-sync是高危操作——它可能卸载掉你 IDE如 PyCharm或 Jupyter 所需的核心包。正确姿势是创建干净虚拟环境# 创建虚拟环境使用系统 Python 的 venv 模块 python -m venv C:\pyinst-baseline\venv # 激活Windows C:\pyinst-baseline\venv\Scripts\activate.bat # 此时命令行前缀变为 (venv) C:\pyinst-baseline # 再执行 pip-sync pip-sync requirements-lock.txt激活后所有pip和pyinstaller命令均作用于该隔离环境与系统 Python 完全解耦。这是专业部署的底线习惯。3.4 当你的项目需要额外包时如何安全扩展依赖不要直接pip install my-new-package这会破坏requirements-lock.txt的完整性。正确流程是将新包添加到requirements.in需自行创建# requirements.in requests2.28.1 numpy1.21.0 my-new-package1.0.0用pip-compile重新生成锁文件pip-compile --generate-hashes requirements.in -o requirements-lock.txt再执行pip-sync requirements-lock.txt这样my-new-package的所有子依赖如pyyaml,six也会被自动解析并加入锁文件保证全链路可重现。4. 自定义 Hook 与资源打包解决 matplotlib、PyQt、OpenCV 等常见黑匣子4.1 为什么你需要自定义 Hook——PyInstaller 的静态分析盲区PyInstaller 通过 AST 解析 Python 源码来收集导入语句。但像matplotlib这样的库其 backend 是运行时动态加载的import matplotlib; matplotlib.use(Agg)AST 中根本看不到backend_agg模块PyQt5的.dll文件、OpenCV的cv2.cp39-win_amd64.pyd旁的opencv_videoio_ffmpeg*.dll也都不会出现在import语句中。这就是为什么你打包后运行报ModuleNotFoundError: No module named matplotlib.backends.backend_agg或cv2.error: OpenCV(4.5.5) ... error: (-215:Assertion failed) !_src.empty() in function cv::cvtColor。pyinstaller依赖包.zip中的hooks/目录就是为这些场景准备的「补丁集」。4.2 如何启用预置 Hook复制到项目同级或 PyInstaller 默认路径PyInstaller 查找 hook 的优先级顺序为当前目录下的hook-module.py--additional-hooks-dir指定的目录PyInstaller 安装目录内的hooks/子目录最稳妥的方式是将pyinstaller依赖包.zip中的hooks/整个目录复制到你的项目根目录# 假设你的项目在 D:\myproject\ copy C:\pyinst-baseline\hooks D:\myproject\ /E /I # 结构变为D:\myproject\hooks\hook-matplotlib.py, D:\myproject\hooks\hook-pywin32.py然后打包时显式指定pyinstaller --additional-hooks-dir./hooks --onefile main.py4.3 关键 Hook 文件功能详解以 matplotlib 和 pywin32 为例hooks/hook-matplotlib.py的核心逻辑是# hook-matplotlib.py from PyInstaller.utils.hooks import collect_data_files, collect_dynamic_libs # 1. 收集所有 backend 模块避免运行时报 backend not found backends [ backend_agg, backend_pdf, backend_ps, backend_svg, ] for backend in backends: datas collect_data_files(matplotlib.backends. backend) # 2. 收集 matplotlib 的数据文件字体、样式表 datas collect_data_files(matplotlib, includes[mpl-data]) # 3. 收集动态库如 Qt backend 依赖的 dll binaries collect_dynamic_libs(matplotlib)hooks/hook-pywin32.py则解决pywin32的经典问题它安装时会向系统注册 COM 组件并在site-packages\pywin32_system32\下放置pythoncom39.dll和pywintypes39.dll。PyInstaller 默认不扫描此目录导致打包后ImportError: DLL load failed while importing win32api。该 hook 显式声明# hook-pywin32.py from PyInstaller.utils.hooks import collect_dynamic_libs binaries collect_dynamic_libs(pywin32)4.4 打包非 Python 资源--add-data 的正确用法与陷阱很多项目包含图片、配置文件、模型权重等。--add-data参数格式为源路径;目标路径Windows 用分号Linux/macOS 用冒号且目标路径是相对于生成的 exe 的运行时路径不是源码路径。错误示范路径写错# ❌ 错误src/assets/ 在打包时不存在且目标路径用了正斜杠 pyinstaller --add-data src/assets;assets main.py正确步骤确保资源文件在项目目录中存在如D:\myproject\assets\icon.png使用os.path.join(sys._MEIPASS, assets, icon.png)在代码中读取sys._MEIPASS是 PyInstaller 解压临时资源的路径打包命令# ✅ 正确源路径是相对当前目录的目标路径用反斜杠Windows pyinstaller --add-data assets;assets --onefile main.py验证是否成功打包后检查dist/main/目录下是否存在assets/子目录及其中文件。5. 常见问题排查5 条血泪经验总结的翻车现场与解法5.1 现象打包成功但运行 exe 时弹窗报错 “Failed to execute script main”原因PyInstaller 默认捕获所有异常并静默退出不显示具体 traceback。你看到的只是顶层包装器的失败真实错误被吞掉了。解决添加--console参数强制显示控制台窗口让 Python 错误堆栈直接打印出来或在main.py开头插入调试钩子import sys, traceback sys.excepthook lambda *args: traceback.print_exception(*args) or input(Press Enter...)这样即使无控制台也能看到完整错误。5.2 现象打包后 exe 运行报 “No module named pkg_resources 或 “No module named setuptools原因pkg_resources是setuptools的一部分但 PyInstaller 5.0 默认不再自动包含setuptools尤其当你的代码中用了pkg_resources.resource_string()加载数据时。解决在requirements-lock.txt中显式添加setuptools58.1.0与 PyInstaller 5.13 兼容的稳定版或打包时加参数--hidden-importsetuptools --hidden-importpkg_resources。5.3 现象使用--onefile打包的 exe 在首次运行极慢10 秒以上之后正常原因--onefile会将所有依赖打包进一个 exe运行时需解压到临时目录%TEMP%\_MEIXXXXXX再执行。首次解压耗时且杀毒软件可能扫描该临时目录导致卡顿。解决不治本但有效用--upx-excludepython39.dll若用 UPX 压缩避免压缩核心 DLL治本方案改用--onedir模式生成dist/main/目录虽多一个文件夹但启动零延迟且便于调试终极方案在代码中预热解压不推荐增加复杂度。5.4 现象打包 PyQt5/6 应用exe 运行后界面空白或报 “Could not find Qt platform plugin”原因PyInstaller 未自动收集 Qt 的platforms插件目录如qwindows.dll或收集到了但路径不对。解决确认已启用hooks/hook-PyQt5.py或hook-PyQt6.py手动添加插件路径--add-binary C:/Python39/Lib/site-packages/PyQt5/Qt5/plugins;PyQt5/Qt5/plugins更可靠方式在代码中设置环境变量打包前import os, sys if getattr(sys, frozen, False): # 运行时exe 解压路径 base_path sys._MEIPASS os.environ[QT_QPA_PLATFORM_PLUGIN_PATH] os.path.join(base_path, PyQt5, Qt5, plugins)5.5 现象打包含logging.config.fileConfig()的项目运行时提示 “No such file or directory: logging.conf原因fileConfig()读取的是文件系统路径但logging.conf被--add-data打包后实际位于sys._MEIPASS下而非原始路径。解决绝对不要把配置文件路径写死为logging.conf必须使用运行时路径import os, sys from logging.config import fileConfig if getattr(sys, frozen, False): config_path os.path.join(sys._MEIPASS, logging.conf) else: config_path logging.conf fileConfig(config_path)并确保打包时已添加--add-data logging.conf;..表示解压到 exe 同级目录。6. 验证打包结果从生成文件结构到运行时行为的全流程检查6.1 第一步检查 dist/ 目录结构是否符合预期打包命令执行完毕后首先进入dist/目录观察生成物。以--onefile为例应看到dist\ └── main.exe # 主可执行文件大小通常 8–15MB含 Python 解释器所有依赖以--onedir为例则是dist\ └── main\ ├── main.exe # 启动器很小约 100KB ├── python39.dll # Python 核心 DLL ├── _internal\ # 解压后的所有 Python 包含 PyInstaller 自身 │ ├── PyInstaller\ │ ├── requests\ │ └── ... ├── assets\ # 你用 --add-data 添加的资源 └── logging.conf # 你添加的配置文件提示若dist/main/下没有_internal目录或main.exe大小仅 100KB说明--onedir未生效可能是参数拼写错误如--one-dir是错的正确是--onedir。6.2 第二步用 Dependency Walker 或 Process Monitor 看动态依赖main.exe能否运行不仅取决于 Python 层面的模块更取决于 Windows 层面的 DLL。一个经典翻车是pywin32的pythoncom39.dll被打包进_internal但main.exe启动时仍去系统目录找VCRUNTIME140.dll而目标机器没装 VC 运行库。验证方法Dependency Walker (depends.exe)打开dist/main.exe查看右侧列表是否有红色标记的 DLL如VCRUNTIME140.dll,MSVCP140.dll。若有需将对应vcruntime140.dll从C:\Windows\System32\复制到dist/main/目录并用--add-binary打包Process Monitor (ProcMon)运行main.exe同时开启 ProcMon过滤Process Name为main.exe观察Result列中是否有NAME NOT FOUND定位缺失的 DLL 或文件。6.3 第三步在干净虚拟机中测试——唯一可信的验收标准本地能跑 ≠ 用户能跑。最可靠的验证是在一台全新安装的 Windows 10/11 虚拟机未装任何 Python、VS、Git中测试下载main.exe或整个dist/main/文件夹双击运行观察是否弹出控制台、是否正常打印日志、是否能加载图片/写入文件/连接网络若失败立即截图错误信息——这才是真实用户看到的。我一般会维护一个test-vm快照每次打包新版本前都强制还原此快照再测试。从那以后我每次交付 exe 前都强制走一遍虚拟机测试哪怕多花 3 分钟。因为用户不会告诉你“你的程序在我电脑上少个 DLL”他们只会说“打不开”然后删掉你的软件。6.4 第四步自动化验证脚本——把人工检查变成一行命令为避免重复劳动我在build-tools\下写了verify-dist.ps1PowerShell# verify-dist.ps1 param([string]$ExePath .\dist\main.exe) if (-not (Test-Path $ExePath)) { Write-Error Exe not found: $ExePath; exit 1 } # 1. 检查文件大小过小可能打包失败 $size (Get-Item $ExePath).Length / 1MB if ($size -lt 5) { Write-Warning Exe too small: $size MB } # 2. 启动并等待 5 秒检查是否崩溃 $proc Start-Process $ExePath -PassThru Start-Sleep -Seconds 5 if ($proc.HasExited -and $proc.ExitCode -ne 0) { Write-Error Exe crashed with exit code $($proc.ExitCode) exit 1 } # 3. 检查 dist/ 下是否存在预期资源 if (-not (Test-Path .\dist\main\assets)) { Write-Warning assets folder missing } Write-Host ✅ Verification passed.在 CI 流程中只需pwsh .\build-tools\verify-dist.ps1即可完成基础健康检查。希望帮到你。本文还有配套的精品资源点击获取