PyInstaller 打包后报错 No module named ‘serial‘ 的深度排查与根治

标签:#Python #PyInstaller #Conda #pyserial #打包踩坑 #疑难杂症

如果你也是用 Conda (Miniconda/Anaconda) 管理环境,当你满心欢喜地写完一个串口工具,用 pyinstaller -F -w xxx.py 打包,结果双击 exe 直接闪退,或者在 CMD 里运行看到这一行:

ModuleNotFoundError: No module named 'serial'

请不要怀疑人生,这不是你的代码问题,也不是 pyserial 没装,而是 PyInstaller 和 Conda 这对冤家在打架。

本文将记录我是如何一步步被坑,又如何彻底解决这个问题的。

一、事故现场还原

  1. 开发环境

• Python 环境:Miniconda3

• 操作系统:Windows 10/11

• 关键库:pyserial (3.5)

  1. 代码逻辑

一个简单的 Tkinter 串口上位机,使用 serial.tools.list_ports 枚举串口。

  1. 打包指令(错误示范)

一开始我用的都是网上搜到的"标准答案":

pyinstaller -F -w mouse_keyboard_host.py

或者加了隐藏导入:

pyinstaller -F -w --hidden-import serial mouse_keyboard_host.py

  1. 报错结果

无论怎么打,生成的 exe 都无法运行,报错始终是:

ModuleNotFoundError: No module named 'serial'

二、为什么 pip show 明明有,PyInstaller 却找不到?

这是最让人困惑的地方。明明终端里查得到:

pip show pyserial

输出:

F:\MouseTester>pip show pyserial

Name: pyserial

Version: 3.5

Summary: Python Serial Port Extension

Home-page: https://github.com/pyserial/pyserial

Author: Chris Liechti

Author-email: cliechti@gmx.net

License: BSD

Location: d:\miniconda3\lib\site-packages

Requires:

Required-by:

关键点在这里:

Location: d:\miniconda3\lib\site-packages

原因其实很"蠢":

  1. PyInstaller 的搜索路径(Path)机制比较死板。
  2. 当你使用 Conda 时,Python 安装在非标准路径(如 d:\miniconda3...)。
  3. PyInstaller 在打包时,并没有去 Conda 的 site-packages 里找库,或者只找到了一半。
  4. 特别是 pyserial 这个库,它的导入方式很特殊(import serial),但实际包名是 pyserial,且包含很多动态加载的子模块(如 serial.tools.list_ports),PyInstaller 的自动分析经常会漏掉。

三、根治方案(Conda 用户必看)

不要再试那些没用的参数了,直接上终极组合拳。

步骤 1:清理战场

先把之前生成的垃圾文件删掉,避免缓存干扰:

rmdir /s /q build dist pycache

步骤 2:使用绝对路径 + 强制收集

这是唯一能解决问题的命令。请根据你的 pip show pyserial 结果修改路径。

pyinstaller -F -w ^

-p "d:\miniconda3\lib\site-packages" ^

--hidden-import=serial ^

--hidden-import=serial.tools.list_ports ^

--collect-all pyserial ^

mouse_keyboard_host.py

关键参数解析(划重点)

参数 作用 为什么必须加

-p "d:\miniconda3..." 指定搜索路径 强制 PyInstaller 去 Conda 的库目录里找东西。

--hidden-import=serial 显式声明主模块 防止 PyInstaller 漏掉主入口。

--hidden-import=serial.tools.list_ports 显式声明子模块 你的代码里用了 list_ports,必须打进去。

--collect-all pyserial 核武器 强制把 pyserial 包下的所有文件、依赖、子模块一股脑全打进 exe。

四、验证结果

执行完上述命令后,你应该能看到:

562 INFO: Building EXE from EXE-00.toc completed successfully.

进入 dist 目录,双击 mouse_keyboard_host.exe。

✅ 界面正常弹出

✅ 串口列表正常加载

✅ 在其他没装 Python 的机器上也能跑

五、总结

  1. Conda 环境打包,必须加 -p 参数指定 site-packages 路径。
  2. pyserial 是个坑货,光靠 --hidden-import 不够,必须用 --collect-all。
  3. 不要迷信网上的简单教程,很多时候那是针对 Virtualenv 或系统 Python 的,不适用于 Conda。

希望这篇博客能救你于水火之中,少熬几个夜。🚀