当我们利用Python开发好法式需要打包成exe时,支流的做法即是利用pyinstaller,那玩意,看似简单,其实挺费事的,坑比力多,出格是涉及到比力复杂的库时,别的一个费事的工作是,打包失败后,搜刮到的良多处理计划是没有效果的。
前一段时间,我用Python开发了视频同步助手,也是用pyinstaller打包的,此中涉及到opencv-python、ffmpeg、moviepy等包,嗯,那个过程比力磨人,在我共同pyinstaller源码与其文档后,掌握了一些技巧,本文简单总结记录一下,希望对你有所帮忙。
动态导入问题
若是你项目中利用了opencv-python库,简单操纵pyinstaller打包,很容易呈现打包胜利了,却无法运行exe的情况,如下图:
从报错细节来看,它让你查抄OpenCV能否安拆(Check OpenCV installation),但那其实不是报错原因,核心在那句:
native_module = importlib.import_module( "cv2")
展开全文
importlib库在营业型项目中是比力少利用的,其感化就是动态载入响应的库,而我们在日常的营业开发中,利用import关键字来实现库的载入。
良多Python开源项目会利用importlib来实现插件系统,值得进修,但那里却因为importlib的原因,让pyinstaller打包失败。
阅读pyinstaller文档中的【What PyInstaller Does and How It Does It】末节,可知,pyinstaller在打包时,会将项目标依赖也打包进来,但不包罗下面几种情况:
实现了__import__办法的类实例,在项目中利用时,无法被pyinstaller检测
通过importlib.import_module办法导入的库,无法被pyinstaller检测
通过sys.path施行的逻辑,无法被pyinstaller检测
嗯,pyinstaller存在那些局限,而良多出名的库却大量呈现上面的三种情况,好比Django、opencv-python。
怎么办?文档给出了4种处理计划:
通过pyinstaller号令行打包时,通过响应的设置装备摆设参数,给出额外的信息
将项目修改成利用import关键字导入的形式
编写spec文件,给出额外信息,那与第1种办法不异,号令行上指定的参数,等价于spec设置装备摆设文件中的设置装备摆设
利用hook,实现动态替代
起首排除办法2,因为那种体例只适用于你本身的项目,而Django、opencv-python那类第三方库,改不动,改动了也欠好维护。
然后排除办法1与办法3,关于简单情况,那两种办法是能够的,文本后面点也会介绍,但一些第三方库,动态导入的处所比力多,你通过写死设置装备摆设的形式不太靠谱。
嗯,剩下办法4了。
什么是pyinstaller的hook?其实就是动态替代一些信息的一种办法。以opencv-python为例,开发者本身晓得差别版本的opencv-python动态导入时,会导入什么处所的数据,通过hook的形式,在不改动opencv-python的根底上,动态映射成我们本身的导入体例。
pyinstaller文档中给出了hook的开发细节,但不消急着脱手,pyinstaller的社区已经将一些出名库的hook都开发好了,当你安拆好pyinstaller时,响应的hook库其实也安拆好了,叫pyinstaller-hooks-contrib。
pyinstaller-hooks-contrib 是社区维护的pyinstaller hooks机造
我们以opencv-python为例,找到opencv-python代码动态导入的位置,如下图:
当我们打包opencv-python时,需要留意opencv-python的版本,因为差别版本的opencv-python,需要hook的位置可能会改动,我们看到pyinstaller opencv-python相关的hook代码中的正文也能够看出其版本要求:
颠末屡次尝试,下面的版本关系能够让opencv-python胜利打包。
pip uninstall pyinstaller-hooks-contrib
pip install pyinstaller-hooks-contrib== 2021.3
pip uninstall pyinstaller
pip install pyinstaller== 4.5.1
pip uninstall opencv-python
pip install opencv-python== 4.5.4.58
但,单纯的处理版本问题,仍是无法很好的利用opencv-python,我们还需要将opencv-python的完好途径告诉pyinstaller,那需要利用办法1或办法3,我小我习惯利用办法3,即操纵spec设置装备摆设文件的形式来给pyinstaller更多额外信息。
spec文件
阅读pyinstaller文档中的【Using Spec Files】末节可知,spec文件会告诉pyinstaller打包时,若何处置被打包脚本,且spec文件现实上是可施行的python代码。
从文档可知,spec文件次要有4个用处:
当你希望将数据文件与打包法式绑缚在一路时
当你希望包罗运行时库时(DLL、SO等文件)
当你希望将Python run-time options添加到可施行文件时
当您想创建一个包罗合并的公共模块的多法式包时
用处3与用处4没有在现实项目中利用过,所以不讨论,我们次要来看看用处1与用处2。
我们能够利用下面号令创建spec文件:
pyi-makespec main.py
下面是【无感视频同步助手】的spec文件,比拟于创建出的默认spec文件,内容多会多一些,建议你间接从我那里复造进来用。
# -*- mode: python ; coding: utf-8 -*-
importjson
importos
importsys
importPyInstaller.config
# 存放最末打包成app的相对途径
buildPath = 'build'
PyInstaller.config.CONF[ 'distpath'] = buildPath
# 存放打包成app的中间文件的相对途径
cachePath = os.path.join(buildPath, 'cache')
ifnotos.path.exists(cachePath):
os.makedirs(cachePath)
PyInstaller.config.CONF[ 'workpath'] = cachePath
# icon相对途径
icoPath = os.path.join( 'logo.ico')
# 项目名称
appName = '无感视频同步助手'
# 版本号
version = '1.0.0'
# 对Python字节码加密
block_cipher = pyi_crypto.PyiBlockCipher(key= '875650321356')
a = Analysis([ 'gui_main.py'],
pathex=[ "venv\\Lib\\site-packages\\cv2"],
binaries=[( "venv\\Lib\\site-packages\\cv2\\opencv_videoio_ffmpeg453_64.dll", ".")],
datas=[( 'gui\\frontend', 'gui\\frontend')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects= False,
win_private_assemblies= False,
cipher=block_cipher,
noarchive= False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.s,
a.binaries,
a.zipfiles,
a.datas,
name=appName,
debug= False,
bootloader_ignore_signals= False,
strip= False,
upx= True,
upx_exclude=[],
runtime_tmpdir= None,
console= False,
disable_windowed_traceback= False,
target_arch= None,
codesign_identity= None,
entitlements_file= None,
icon=icoPath)
此中:
pathex=[ "venv\\Lib\\site-packages\\cv2"],
即是将opencv-python完好项目标途径告诉pyinstaller,如许打包pyinstaller-python时,再共同上准确的pyinstaller与opencv-python版本,即可以打包出可一般翻开的exe。
常识点:第三方库代码相关的放在pathex字段中
打包后的opencv-python无法处置视频
一切似乎很ok,但实正运行营业逻辑时,会报错:
颠末加日记重打包后阐发可知,它鄙人面位置报错:
opencv-python处置视频其实操纵了ffmpeg.dll,而我们打包时,若是没有告诉pyinstaller ffmpeg.dll的位置,pyinstaller就不会将其打包进来,则会招致运行报错。
所以,spec文件中需要下面的内容:
binaries=[( "venv\\Lib\\site-packages\\cv2\\opencv_videoio_ffmpeg453_64.dll", ".")],
常识点:dll、so那类动态库,要写在binaries字段中。
静态资本打包
【无感视频同步助手】利用了html、css来做规划,那些不是python代码,对python而言,类似于image、video之类的静态资本,那类静态资本,我们需要写到spec文件的datas字段中:
datas=[( 'gui\\frontend', 'gui\\frontend')],
打包moviepy
搞定opencv-python后,你能够用类似的办法来搞moviepy那个库,究竟结果moviepy也是基于ffmpeg来弄的,那不简单。
嗯,不会灵敏变通的话,可能会懵逼,因为moviepy有如下导入体例,且社区没有供给moviepy的hook:
moviepy的做者偷懒,间接通过exec来批量导入需要的库,不成为不骚。
怎么处理?
利用办法2,没错,将其改成利用import关键字导入的形式,但不是改moviepy的代码。我们创建moviepy_import.py文件,将需要导入的库都写进去。
然后再项目入口py文件中,import moviepy_import,处理moviepy批量导入的骚写法。
此外,moviepy打包还有别的一个问题,因为moviepy利用了imageio_ffmpeg那个库,而imageio_ffmpeg会利用ffmpeg,但我们打包时,没有将ffmpeg文件打包进去,moviepy在运行时便会报错。
阅读imageio_ffmpeg目次,发现它本身会安拆对应版本的ffmpeg。
找到moviepy报错位置,其实是imageio_ffmpeg库的_utils.py文件中的get_ffmpeg_exe办法,如下图:
其实就是找不到ffmpeg而报错,我的处理办法是手动设置一下:
结尾
嗯,目前我条记里有记录的坑就上文中那些了,一个别会是,阅读源码和阅读文档的才能很重要,出格是材料比力少的情况。
入门: 最全的零根底学Python的问题 | 零根底学了8个月的Python |实战项目 | 学Python就是那条捷径
干货:爬取豆瓣短评,片子《后来的我们》 | 38年NBA更佳球员阐发 |从万寡等待到口碑扑街!唐探3令人绝望 | 笑看新倚天屠龙记 | 灯谜答题王 | 用Python做个海量蜜斯姐素描图 | 碟中谍那么火,我用机器进修做个迷你保举系统片子
兴趣:弹球游戏 | 九宫格 | 标致的花 | 两百行Python《天天酷跑》游戏!
AI:会做诗的机器人 | 给图片上色 | 预测收入 | 碟中谍那么火,我用机器进修做个迷你保举系统片子
小东西: Pdf转Word,轻松搞定表格和水印! | 一键把html网页保留为pdf! |再见PDF提取收费! | 用90行代码打造最强PDF转换器,word、PPT、excel、markdown、html一键转换 | 造做一款钉钉低价机票提醒器! |60行代码做了一个语音壁纸切换器天天看蜜斯姐! |
年度爆款案牍
1). 卧槽!Pdf转Word用Python轻松搞定 !
2).学Python实香!我用100行代码做了个网站,帮人PS游览图片,赚个鸡腿吃
3).首播过亿,火爆全网,我阐发了《披荆斩棘的姐姐》,发现了那些奥秘
4). 80行代码!用Python做一个哆来A梦分身
5).你必需掌握的20个python代码,短小精悍,用途无限
6). 30个Python奇淫技巧集
7). 我总结的80页《菜鸟学Python精选干货.pdf》,都是干货
8). 再见Python!我要学Go了!2500字深度阐发 !
9).发现一个舔狗福利!那个Python爬虫神器太爽了,主动下载妹子图片