<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          實(shí)用技巧!Pyinstaller打包技巧!

          共 7985字,需瀏覽 16分鐘

           ·

          2022-11-08 17:17


          當(dāng)我們使用Python開(kāi)發(fā)好程序需要打包成exe時(shí),主流的做法便是使用pyinstaller,這玩意,看似簡(jiǎn)單,其實(shí)挺麻煩的,坑比較多,特別是涉及到比較復(fù)雜的庫(kù)時(shí),另外一個(gè)麻煩的事情是,打包失敗后,搜索到的很多解決方案是沒(méi)有效果的。

          前一段時(shí)間,我用Python開(kāi)發(fā)了視頻同步助手,也是用pyinstaller打包的,其中涉及到opencv-python、ffmpeg、moviepy等包,嗯,這個(gè)過(guò)程比較磨人,在我配合pyinstaller源碼與其文檔后,掌握了一些技巧,本文簡(jiǎn)單總結(jié)記錄一下,希望對(duì)你有所幫助。

          動(dòng)態(tài)導(dǎo)入問(wèn)題

          如果你項(xiàng)目中使用了opencv-python庫(kù),簡(jiǎn)單利用pyinstaller打包,很容易出現(xiàn)打包成功了,卻無(wú)法運(yùn)行exe的情況,如下圖:

          從報(bào)錯(cuò)細(xì)節(jié)來(lái)看,它讓你檢查OpenCV是否安裝(Check OpenCV installation),但這其實(shí)不是報(bào)錯(cuò)原因,核心在這句:

          native_module = importlib.import_module("cv2")

          importlib庫(kù)在業(yè)務(wù)型項(xiàng)目中是比較少使用的,其作用就是動(dòng)態(tài)載入相應(yīng)的庫(kù),而我們?cè)谌粘5臉I(yè)務(wù)開(kāi)發(fā)中,使用import關(guān)鍵字來(lái)實(shí)現(xiàn)庫(kù)的載入。

          很多Python開(kāi)源項(xiàng)目會(huì)使用importlib來(lái)實(shí)現(xiàn)插件系統(tǒng),值得學(xué)習(xí),但這里卻因?yàn)閕mportlib的原因,讓pyinstaller打包失敗。

          閱讀pyinstaller文檔中的【W(wǎng)hat PyInstaller Does and How It Does It】小節(jié),可知,pyinstaller在打包時(shí),會(huì)將項(xiàng)目的依賴也打包進(jìn)來(lái),但不包含下面幾種情況:

          1. 實(shí)現(xiàn)了__import__()方法的類實(shí)例,在項(xiàng)目中使用時(shí),無(wú)法被pyinstaller檢測(cè)
          2. 通過(guò)importlib.import_module()方法導(dǎo)入的庫(kù),無(wú)法被pyinstaller檢測(cè)
          3. 通過(guò)sys.path執(zhí)行的邏輯,無(wú)法被pyinstaller檢測(cè)

          嗯,pyinstaller存在這些局限,而很多知名的庫(kù)卻大量出現(xiàn)上面的三種情況,比如Django、opencv-python。

          怎么辦?文檔給出了4種解決方案:

          1. 通過(guò)pyinstaller命令行打包時(shí),通過(guò)相應(yīng)的配置參數(shù),給出額外的信息
          2. 將項(xiàng)目修改成使用import關(guān)鍵字導(dǎo)入的形式
          3. 編寫(xiě)spec文件,給出額外信息,這與第1種方法相同,命令行上指定的參數(shù),等價(jià)于spec配置文件中的配置
          4. 使用hook,實(shí)現(xiàn)動(dòng)態(tài)替換

          首先排除方法2,因?yàn)檫@種方式只適用于你自己的項(xiàng)目,而Django、opencv-python這類第三方庫(kù),改不動(dòng),改動(dòng)了也不好維護(hù)。

          然后排除方法1與方法3,對(duì)于簡(jiǎn)單情況,這兩種方法是可以的,文本后面點(diǎn)也會(huì)介紹,但一些第三方庫(kù),動(dòng)態(tài)導(dǎo)入的地方比較多,你通過(guò)寫(xiě)死配置的形式不太靠譜。

          嗯,剩下方法4了。

          什么是pyinstaller的hook?其實(shí)就是動(dòng)態(tài)替換一些信息的一種方法。以opencv-python為例,開(kāi)發(fā)者自己知道不同版本的opencv-python動(dòng)態(tài)導(dǎo)入時(shí),會(huì)導(dǎo)入什么地方的數(shù)據(jù),通過(guò)hook的形式,在不改動(dòng)opencv-python的基礎(chǔ)上,動(dòng)態(tài)映射成我們自己的導(dǎo)入方式。

          pyinstaller文檔中給出了hook的開(kāi)發(fā)細(xì)節(jié),但不用急著動(dòng)手,pyinstaller的社區(qū)已經(jīng)將一些知名庫(kù)的hook都開(kāi)發(fā)好了,當(dāng)你安裝好pyinstaller時(shí),相應(yīng)的hook庫(kù)其實(shí)也安裝好了,叫pyinstaller-hooks-contrib。

          pyinstaller-hooks-contrib 是社區(qū)維護(hù)的pyinstaller hooks機(jī)制

          我們以opencv-python為例,找到opencv-python代碼動(dòng)態(tài)導(dǎo)入的位置,如下圖:

          當(dāng)我們打包opencv-python時(shí),需要注意opencv-python的版本,因?yàn)椴煌姹镜膐pencv-python,需要hook的位置可能會(huì)改變,我們看到pyinstaller opencv-python相關(guān)的hook代碼中的注釋也可以看出其版本要求:

          經(jīng)過(guò)多次實(shí)驗(yàn),下面的版本關(guān)系可以讓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  

          但,單純的解決版本問(wèn)題,還是無(wú)法很好的使用opencv-python,我們還需要將opencv-python的完整路徑告訴pyinstaller,這需要使用方法1或方法3,我個(gè)人習(xí)慣使用方法3,即利用spec配置文件的形式來(lái)給pyinstaller更多額外信息。

          spec文件

          閱讀pyinstaller文檔中的【Using Spec Files】小節(jié)可知,spec文件會(huì)告訴pyinstaller打包時(shí),如何處理被打包腳本,且spec文件實(shí)際上是可執(zhí)行的python代碼。

          從文檔可知,spec文件主要有4個(gè)用途:

          1. 當(dāng)你希望將數(shù)據(jù)文件與打包程序捆綁在一起時(shí)
          2. 當(dāng)你希望包含運(yùn)行時(shí)庫(kù)時(shí)(DLL、SO等文件)
          3. 當(dāng)你希望將Python run-time options添加到可執(zhí)行文件時(shí)
          4. 當(dāng)您想創(chuàng)建一個(gè)包含合并的公共模塊的多程序包時(shí)

          用途3與用途4沒(méi)有在實(shí)際項(xiàng)目中使用過(guò),所以不討論,我們主要來(lái)看看用途1與用途2。

          我們可以使用下面命令創(chuàng)建spec文件:

          pyi-makespec main.py

          下面是【無(wú)感視頻同步助手】的spec文件,相比于創(chuàng)建出的默認(rèn)spec文件,內(nèi)容多會(huì)多一些,建議你直接從我這里復(fù)制出去用。


          # -*- mode: python ; coding: utf-8 -*-

          import json
          import os
          import sys

          import PyInstaller.config

          # 存放最終打包成app的相對(duì)路徑
          buildPath = 'build'
          PyInstaller.config.CONF['distpath'] = buildPath

          # 存放打包成app的中間文件的相對(duì)路徑
          cachePath = os.path.join(buildPath, 'cache')
          if not os.path.exists(cachePath):
              os.makedirs(cachePath)
          PyInstaller.config.CONF['workpath'] = cachePath

          # icon相對(duì)路徑
          icoPath = os.path.join('logo.ico')

          # 項(xiàng)目名稱
          appName = '無(wú)感視頻同步助手'

          # 版本號(hào)
          version = '1.0.0'

          # 對(duì)Python字節(jié)碼加密
          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.scripts,
                  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完整項(xiàng)目的路徑告訴pyinstaller,這樣打包pyinstaller-python時(shí),再配合上正確的pyinstaller與opencv-python版本,便可以打包出可正常打開(kāi)的exe。

          知識(shí)點(diǎn):第三方庫(kù)代碼相關(guān)的放在pathex字段中

          打包后的opencv-python無(wú)法處理視頻

          一切似乎很ok,但真正運(yùn)行業(yè)務(wù)邏輯時(shí),會(huì)報(bào)錯(cuò):

          經(jīng)過(guò)加日志重打包后分析可知,它在下面位置報(bào)錯(cuò):

          opencv-python處理視頻其實(shí)利用了ffmpeg.dll,而我們打包時(shí),如果沒(méi)有告訴pyinstaller ffmpeg.dll的位置,pyinstaller就不會(huì)將其打包進(jìn)來(lái),則會(huì)導(dǎo)致運(yùn)行報(bào)錯(cuò)。

          所以,spec文件中需要下面的內(nèi)容:

          binaries=[("venv\\Lib\\site-packages\\cv2\\opencv_videoio_ffmpeg453_64.dll"".")],

          知識(shí)點(diǎn):dll、so這類動(dòng)態(tài)庫(kù),要寫(xiě)在binaries字段中。

          靜態(tài)資源打包

          【無(wú)感視頻同步助手】使用了html、css來(lái)做布局,這些不是python代碼,對(duì)python而言,類似于image、video之類的靜態(tài)資源,這類靜態(tài)資源,我們需要寫(xiě)到spec文件的datas字段中:

           datas=[('gui\\frontend''gui\\frontend')],

          打包moviepy

          搞定opencv-python后,你可以用類似的方法來(lái)搞moviepy這個(gè)庫(kù),畢竟moviepy也是基于ffmpeg來(lái)弄的,這不簡(jiǎn)單。

          嗯,不會(huì)靈活變通的話,可能會(huì)懵逼,因?yàn)閙oviepy有如下導(dǎo)入方式,且社區(qū)沒(méi)有提供moviepy的hook:

          moviepy的作者偷懶,直接通過(guò)exec來(lái)批量導(dǎo)入需要的庫(kù),不可為不騷。

          怎么解決?

          使用方法2,沒(méi)錯(cuò),將其改成使用import關(guān)鍵字導(dǎo)入的形式,但不是改moviepy的代碼。我們創(chuàng)建moviepy_import.py文件,將需要導(dǎo)入的庫(kù)都寫(xiě)進(jìn)去。

          然后再項(xiàng)目入口py文件中,import moviepy_import,解決moviepy批量導(dǎo)入的騷寫(xiě)法。

          此外,moviepy打包還有另外一個(gè)問(wèn)題,因?yàn)閙oviepy使用了imageio_ffmpeg這個(gè)庫(kù),而imageio_ffmpeg會(huì)使用ffmpeg,但我們打包時(shí),沒(méi)有將ffmpeg文件打包進(jìn)去,moviepy在運(yùn)行時(shí)便會(huì)報(bào)錯(cuò)。

          瀏覽imageio_ffmpeg目錄,發(fā)現(xiàn)它自己會(huì)安裝對(duì)應(yīng)版本的ffmpeg。

          找到moviepy報(bào)錯(cuò)位置,其實(shí)是imageio_ffmpeg庫(kù)的_utils.py文件中的get_ffmpeg_exe()方法,如下圖:

          其實(shí)就是找不到ffmpeg而報(bào)錯(cuò),我的解決方法是手動(dòng)設(shè)置一下:

          結(jié)尾

          嗯,目前我筆記里有記錄的坑就上文中這些了,一個(gè)體會(huì)是,閱讀源碼和閱讀文檔的能力很重要,特別是資料比較少的情況。




          推薦閱讀:

          入門: 最全的零基礎(chǔ)學(xué)Python的問(wèn)題  | 零基礎(chǔ)學(xué)了8個(gè)月的Python  | 實(shí)戰(zhàn)項(xiàng)目 |學(xué)Python就是這條捷徑


          干貨:爬取豆瓣短評(píng),電影《后來(lái)的我們》 | 38年NBA最佳球員分析 |   從萬(wàn)眾期待到口碑撲街!唐探3令人失望  | 笑看新倚天屠龍記 | 燈謎答題王 |用Python做個(gè)海量小姐姐素描圖 |碟中諜這么火,我用機(jī)器學(xué)習(xí)做個(gè)迷你推薦系統(tǒng)電影


          趣味:彈球游戲  | 九宮格  | 漂亮的花 | 兩百行Python《天天酷跑》游戲!


          AI: 會(huì)做詩(shī)的機(jī)器人 | 給圖片上色 | 預(yù)測(cè)收入 | 碟中諜這么火,我用機(jī)器學(xué)習(xí)做個(gè)迷你推薦系統(tǒng)電影


          小工具: Pdf轉(zhuǎn)Word,輕松搞定表格和水?。?/a> | 一鍵把html網(wǎng)頁(yè)保存為pdf!|  再見(jiàn)PDF提取收費(fèi)! | 用90行代碼打造最強(qiáng)PDF轉(zhuǎn)換器,word、PPT、excel、markdown、html一鍵轉(zhuǎn)換 | 制作一款釘釘?shù)蛢r(jià)機(jī)票提示器! |60行代碼做了一個(gè)語(yǔ)音壁紙切換器天天看小姐姐!



          年度爆款文案


          點(diǎn)閱讀原文,看B站我的視頻!

          瀏覽 98
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  seseav | 国产内射免费看 | 波多野结衣AV天堂 | 天天干天天射天天舔麻豆 | 激情综合社区 |