前言
前段时间做一个内网开发的需求,要求将selenium程序打包成.exe放在内网的win7上运行,在掘金搜了一圈也没有发现相关文章,因此将过程中踩到的坑记录分享一下。
本文涵盖了具体打包操作、不同模块和依赖项的兼容性解决方案,以确保在打包和运行时都能正常工作。
1. 背景
在 Python 项目中,使用第三方库(如 selenium
、selenium-wire
、mitmproxy
等)时,会遇到许多依赖项,并且由于库之间的版本兼容性问题,可能导致运行时错误。常用的打包工具 PyInstaller
能将 Python 项目打包成单个可执行文件,但也会因为兼容性问题和路径管理而出现各种运行错误。因此,本指南总结了打包过程中常见问题和解决方案,以帮助开发者顺利完成项目的打包和发布。
2. 可能遇到的问题概述
在PyInstaller
打包selenium-wire
时可能会遇到一些问题,如下:
- 依赖冲突 :如
pyOpenSSL
与cryptography
的版本冲突问题。 - 路径问题 :如
chromedriver.exe
在运行时未找到或未正确加载。 - 打包文件缺失 :某些文件(如
.crt
、.key
、chromedriver.exe
等)在打包时未包含,导致运行时无法找到。
3. PyInstaller 打包步骤及参数配置
使用 PyInstaller
打包一个 Python 项目时,可以通过以下步骤和命令来生成可执行文件:
css
pyinstaller --onefile --clean --hidden-import=<module> --name=<executable_name> <script.py>
参数详解:
--onefile
:将所有文件打包成一个独立可执行文件。--clean
:清理之前打包时的缓存,确保使用最新的依赖版本。--hidden-import
:指定打包时包含的隐藏模块(PyInstaller 有时无法自动检测到的依赖)。--name
:指定打包生成的可执行文件名称。
对于使用 .spec
文件的项目,可以通过如下命令打包:
css
pyinstaller --clean <spec_file_name>.spec
4. 依赖项版本不兼容问题
4.1 pyOpenSSL
和 cryptography
的兼容性问题
在 PyInstaller
打包的项目中,pyOpenSSL
和 cryptography
是常见依赖。由于版本更新问题,某些版本的 pyOpenSSL
可能无法与较新版本的 cryptography
兼容,导致运行时 X509_V_FLAG_NOTIFY_POLICY
等属性缺失。
常见错误:
arduino
AttributeError: module 'lib' has no attribute 'X509_V_FLAG_NOTIFY_POLICY'
解决方法:
- 降级
cryptography
版本 :建议降级到3.3.2
版本,确保兼容性。
ini
pip install cryptography==3.3.2
- 降级
pyOpenSSL
版本 :使用20.0.1
版本,这与cryptography 3.3.2
更加兼容。
ini
pip install pyOpenSSL==20.0.1
- 升级所有相关依赖 :如果使用较旧的版本无效,尝试升级
selenium-wire
、mitmproxy
、pyOpenSSL
、和cryptography
,确保依赖版本相互兼容。
sql
pip install --upgrade selenium-wire mitmproxy pyOpenSSL cryptography
4.2 chromedriver.exe
打包问题
selenium
使用的 chromedriver.exe
必须在系统的 PATH
中或由代码显式指定路径。然而,打包成单文件后,chromedriver.exe
可能无法正常找到,需要手动配置。
5. 路径问题及解决方法
5.1 包含 chromedriver.exe
文件
将 chromedriver.exe
文件放在项目目录下,并在 .spec
文件的 datas
配置中包含此文件,以确保在打包后可以正确引用。
配置示例:
- 在
.spec
文件中将chromedriver.exe
添加到datas
:
ini
datas=[
('<absolute_path>/chromedriver.exe', '.') # 打包到可执行文件的根目录
]
- 在代码中设置相对路径以引用
chromedriver.exe
文件,确保在打包后的运行环境中可以正确定位到该文件。
lua
from selenium.webdriver.chrome.service import Service
import os
import sys
def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)
chrome_driver_path = resource_path("chromedriver.exe")
service = Service(executable_path=chrome_driver_path)
6. 详细解决方案
在打包过程中,还可能遇到其他常见问题,例如文件缓存和打包依赖文件丢失问题。
6.1 清理缓存文件
在打包前,通过 --clean
参数或手动删除 build
和 dist
文件夹,确保 PyInstaller
不使用缓存文件:
css
pyinstaller --clean <spec_file_name>.spec
或者手动删除 build
和 dist
文件夹:
bash
rmdir /s /q build
rmdir /s /q dist
6.2 使用 .spec
文件配置 hiddenimports
如果 PyInstaller
在打包时无法自动识别所有依赖,可以通过 .spec
文件中的 hiddenimports
参数显式指定依赖项:
ini
hiddenimports=['mitmproxy', 'seleniumwire', 'OpenSSL', 'cryptography'],
6.3 将证书文件包含在打包中
某些依赖(如 selenium-wire
)使用的 .crt
和 .key
文件也需手动包含:
ini
datas=[
('<absolute_path>/seleniumwire/ca.crt', 'seleniumwire'),
('<absolute_path>/seleniumwire/ca.key', 'seleniumwire')
],
7. 调试建议
- 确保依赖版本一致:在开发和打包环境中使用相同的依赖版本,防止版本不一致带来的兼容性问题。
- 使用虚拟环境:每个项目单独配置虚拟环境,避免全局环境中的其他依赖引发冲突。
- 分步调试:打包前在开发环境中逐步测试依赖是否正常运行。遇到依赖问题,优先使用兼容的版本组合。
调试依赖冲突
使用 pip check
命令检查依赖冲突,并通过 pip freeze
获取依赖列表,以便管理版本:
bash
pip check # 检查依赖冲突
pip freeze > requirements.txt # 保存当前依赖
总结
本指南总结了在使用 PyInstaller
打包 Python 项目时常见的兼容性问题和解决方法。通过以下步骤,可以显著提升打包的成功率:
- 使用兼容的依赖版本,尤其是
pyOpenSSL
和cryptography
。 - 将
chromedriver.exe
等可执行文件显式添加到.spec
文件。 - 在代码中使用
sys._MEIPASS
以正确引用打包后临时解压目录中的文件。
严格遵循这些步骤可以有效避免大多数打包和运行时错误,确保项目在各个环境下稳定运行。