Pyinstaller 打包發(fā)布經(jīng)驗總結(jié)
使用Pyinstaller打包Python項目包含了大量的坑,這篇文章總結(jié)實踐得到的Pyinstaller打包經(jīng)驗。本文的例子為Python3.6代碼,Pyinstaller3.4,在windows下打包為64位和32位版本。
Pyinstaller基本使用方法
Pyinstaller可以通過簡單的命令進行python代碼的打包工作,其基本的命令為:
pyinstaller -option xxx.py
options的詳情可參考官方幫助文檔https://pyinstaller.readthedocs.io/en/stable/usage.html
這邊只介紹用到的option:-d生成一個文件目錄包含可執(zhí)行文件和相關(guān)動態(tài)鏈接庫和資源文件等;-f僅生成一個可執(zhí)行文件
-D, --onedir | Create a one-folder bundle containing an executable (default) |
-F, --onefile | Create a one-file bundled executable. |
對于打包結(jié)果較大的項目,選用-d生成目錄相比單可執(zhí)行文件的打包方式,執(zhí)行速度更快,但包含更加多的文件。本文的例子選中-D方式打包。
Python項目的打包方法
以一個多文件和目錄的Python項目為例,項目文件包含:1.Python源代碼文件;2.圖標資源文件;3.其它資源文件
以圖中項目為例,Python源代碼文件在多個目錄下:bin, lib\app, lib\models, lib\views;圖標資源文件在lib\icon目錄下;其它資源文件在data目錄下,包括文本文件,視頻文件等等。
1.spec文件生成
為了進行自定義配置的打包,首先需要編寫打包的配置文件.spec文件。當使用pyinstaller -d xxx.py時候會生成默認的xxx.spec文件進行默認的打包配置。通過配置spec腳本,并執(zhí)行pyinstaller -d xxx.spec完成自定義的打包。
通過生成spec文件的命令,針對代碼的主程序文件生成打包對應(yīng)的spec文件
pyi-makespec -w xxx.py
打開生成的spec文件,修改其默認腳本,完成自定義打包需要的配置。spec文件是一個python腳本,其默認的結(jié)構(gòu)如下例所示
# -*- mode: python -*- block_cipher = None a = Analysis(['fastplot.py'], pathex=['D:\\install_test\\DAGUI-0.1\\bin'], binaries=[], datas=[], hiddenimports=[], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, exclude_binaries=True, name='fastplot', debug=False, strip=False, upx=True, console=False ) coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, name='fastplot')
spec文件中主要包含4個class: Analysis, PYZ, EXE和COLLECT.
- Analysis以py文件為輸入,它會分析py文件的依賴模塊,并生成相應(yīng)的信息
- PYZ是一個.pyz的壓縮包,包含程序運行需要的所有依賴
- EXE根據(jù)上面兩項生成
- COLLECT生成其他部分的輸出文件夾,COLLECT也可以沒有
2.spec文件配置
首先給出舉例python項目的spec文件配置
# -*- mode: python -*- import sys import os.path as osp sys.setrecursionlimit(5000) block_cipher = None SETUP_DIR = 'D:\\install_test\\FASTPLOT\\' a = Analysis(['fastplot.py', 'frozen_dir.py', 'D:\\install_test\\FASTPLOT\\lib\\app\\start.py', 'D:\\install_test\\FASTPLOT\\lib\\models\\analysis_model.py', 'D:\\install_test\\FASTPLOT\\lib\\models\\datafile_model.py', 'D:\\install_test\\FASTPLOT\\lib\\models\\data_model.py', 'D:\\install_test\\FASTPLOT\\lib\\models\\figure_model.py', 'D:\\install_test\\FASTPLOT\\lib\\models\\time_model.py', 'D:\\install_test\\FASTPLOT\\lib\\models\\mathematics_model.py', 'D:\\install_test\\FASTPLOT\\lib\\views\\constant.py', 'D:\\install_test\\FASTPLOT\\lib\\views\\custom_dialog.py', 'D:\\install_test\\FASTPLOT\\lib\\views\\data_dict_window.py', 'D:\\install_test\\FASTPLOT\\lib\\views\\data_process_window.py', 'D:\\install_test\\FASTPLOT\\lib\\views\\data_sift_window.py', 'D:\\install_test\\FASTPLOT\\lib\\views\\mathematics_window.py', 'D:\\install_test\\FASTPLOT\\lib\\views\\para_temp_window.py', 'D:\\install_test\\FASTPLOT\\lib\\views\\mainwindow.py', 'D:\\install_test\\FASTPLOT\\lib\\views\\paralist_window.py', 'D:\\install_test\\FASTPLOT\\lib\\views\\plot_window.py'], pathex=['D:\\install_test\\FASTPLOT'], binaries=[], datas=[(SETUP_DIR+'lib\\icon','lib\\icon'),(SETUP_DIR+'data','data')], hiddenimports=['pandas','pandas._libs','pandas._libs.tslibs.np_datetime','pandas._libs.tslibs.timedeltas', 'pandas._libs.tslibs.nattype', 'pandas._libs.skiplist','scipy._lib','scipy._lib.messagestream'], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, exclude_binaries=True, name='fastplot', debug=False, strip=False, upx=True, console=True) coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, name='fastplot')
a) py文件打包配置
針對多目錄多文件的python項目,打包時候需要將所有相關(guān)的py文件輸入到Analysis類里。Analysis類中的pathex定義了打包的主目錄,對于在此目錄下的py文件可以只寫文件名不寫路徑。如上的spec腳本,將所有項目中的py文件路徑以列表形式寫入Analysis,這里為了說明混合使用了絕對路徑和相對路徑。
b) 資源文件打包配置
資源文件包括打包的python項目使用的相關(guān)文件,如圖標文件,文本文件等。對于此類資源文件的打包需要設(shè)置Analysis的datas,如例子所示datas接收元組:datas=[(SETUP_DIR+'lib\\icon','lib\\icon'),(SETUP_DIR+'data','data')]。元組的組成為(原項目中資源文件路徑,打包后路徑),例子中的(SETUP_DIR+'lib\\icon','lib\\icon')表示從D:\\install_test\\FASTPLOT\\lib\\icon下的圖標文件打包后放入打包結(jié)果路徑下的lib\\icon目錄。
c)Hidden import配置
pyinstaller在進行打包時,會解析打包的python文件,自動尋找py源文件的依賴模塊。但是pyinstaller解析模塊時可能會遺漏某些模塊(not visible to the analysis phase),造成打包后執(zhí)行程序時出現(xiàn)類似No Module named xxx。這時我們就需要在Analysis下hiddenimports中加入遺漏的模塊,如例子中所示。
d)遞歸深度設(shè)置
在打包導(dǎo)入某些模塊時,常會出現(xiàn)"RecursionError: maximum recursion depth exceeded"的錯誤,這可能是打包時出現(xiàn)了大量的遞歸超出了python預(yù)設(shè)的遞歸深度。因此需要在spec文件上添加遞歸深度的設(shè)置,設(shè)置一個足夠大的值來保證打包的進行,即
import sys sys.setrecursionlimit(5000)
e)去除不必要的模塊import
有時需要讓pyinstaller不打包某些用不到的模塊,可通過在excludes=[]中添加此模塊實現(xiàn),如
excludes=['zmq']
3.使用spec執(zhí)行打包命令
pyinstaller -D xxx.spec
打包生成兩個文件目錄build和dist,build為臨時文件目錄完成打包后可以刪除;dist中存放打包的結(jié)果,可執(zhí)行文件和其它程序運行的關(guān)聯(lián)文件都在這個目錄下。
Visual C++ run-time .dlls包含
針對在Windows<10發(fā)布使用,且Python>=3.5的情況,Pyinstaller打包的程序可能會出現(xiàn)不包含Visual C++ run-time .dlls的情況,Python>=3.5需要使用Visual Studio 2015 run-time,也就是Universal CRT,這些runtime在Win10本身或Win7到Win8.1版本的更新包里,但程序打包后使用的系統(tǒng)里并不一定安裝了。因此需要參考Universal CRT的建議,應(yīng)用以下的方法解決這個問題:
Build on Windows 7 which has been reported to work.
Include one of the VCRedist packages (the redistributable package files) into your application's installer. This is Microsoft's recommended way, see “Distributing Software that uses the Universal CRT“ in the above-mentioned link, numbers 2 and 3.
Install the Windows Software Development Kit (SDK) for Windows 10 and expand the .spec-file to include the required DLLs, see “Distributing Software that uses the Universal CRT“ in the above-mentioned link, number 6.
Python模塊的打包問題
程序調(diào)用的很多包,在打包時候可能會出現(xiàn)一些問題,針對這寫問題需要做一些處理才能保證打包的程序正常執(zhí)行。
1.PyQt plugins缺失
使用PyQt編寫UI交互界面的python代碼在進行打包時可能會出現(xiàn)一些特別的問題。
執(zhí)行使用了PyQt的打包程序,常會出現(xiàn)這樣的錯誤,提示缺少Q(mào)t platfrom plugin “windows”,如下圖
打包后程序運行后,使用png格式的圖標可以正常顯示,但使用的ico格式圖標不顯示(對于所有圖標和關(guān)聯(lián)文件都無法使用的情況涉及到路徑問題,后文會另外解釋)。
這兩個錯誤產(chǎn)生的問題都是因為打包時沒有將PyQt相關(guān)的動態(tài)鏈接庫目錄生成到打包目錄下,因此可以通過將這些需要的文件目錄拷貝到打包生成目錄下,解決plugin缺失問題。以使用PyQt5編寫的python軟件打包為例,完成打包后的結(jié)果目錄下包含PyQt5文件夾,將PyQt5\Qt\plugins下的所有內(nèi)容(如下圖)拷貝到打包結(jié)果目錄。這樣就可以解決PyQt plugins缺失的問題。
2.動態(tài)鏈接庫缺失問題
更一般的,打包后可能會缺失某些動態(tài)鏈接庫,造成執(zhí)行程序出錯,如
ImportError: DLL load failed: 找不到指定的模塊
在打包過程中一般會有與此相關(guān)的warning提示(lib not found)無法找到這些動態(tài)鏈接庫。例如在32位版本的打包中,可能會出現(xiàn)scipy模塊相關(guān)的dll文件無法找到。這時就需要在打包的spec文件中指定動態(tài)鏈接庫路徑,使其關(guān)聯(lián)到打包后的路徑中。
binaries=[('C:\\Program Files\\Python36-32\\Lib\\site-packages\\scipy\\extra-dll','.')]
Analysis下的binaries是為打包文件添加二進制文件,缺失的動態(tài)鏈接庫可以通過這種方式自動加入到打包路徑中。
3.窗體風格變化問題
在某些情況下,如在精簡環(huán)境下的python程序打包中,執(zhí)行打包后的程序會出現(xiàn)窗體風格變?yōu)槔鲜降膚in風格,這是由于打包時候PyQt的styles動態(tài)庫沒有找到。因此只需要在Python 目錄下找到 Lib\site-packages\PyQt5\Qt\plugins\styles,將styles整個目錄復(fù)制到打包結(jié)果目錄。
4.UnicodeDecodeError
當打包時出現(xiàn)類似錯誤時:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xce in position 122
可在打包的命令行中輸入chcp 65001設(shè)置命令行顯示utf-8字符,然后再執(zhí)行打包命令?;蛘?,修改pyinstaller包下的compat.py,根據(jù)報錯對應(yīng)的行將
out = out.decode(encoding)
改為
out = out.decode(encoding, 'replace')
凍結(jié)打包路徑
執(zhí)行打包后的程序,經(jīng)常會出現(xiàn)程序使用的圖標無法顯示,程序使用的關(guān)聯(lián)文件無法關(guān)聯(lián)?;蛘撸诖虬谋緳C上運行正常,但是將打包后的程序放到其它機器上就有問題。這些現(xiàn)象都很有可能是由程序使用的文件路徑發(fā)生改變產(chǎn)生的,因此在打包時候我們需要根據(jù)執(zhí)行路徑進行路徑“凍結(jié)”。
1.使用絕對路徑
在python代碼中使用絕對路徑調(diào)用外部文件可以保證打包時候路徑可追溯,因此在本機上運行打包后程序基本沒問題。但是當本機上對應(yīng)路徑的資源文件被改變,或者將打包程序應(yīng)用到別的機器,都會出現(xiàn)搜索不到資源文件的問題。這種方式不是合適的打包發(fā)布python軟件的方式。
2.使用凍結(jié)路徑
增加一個py文件,例如叫frozen_dir.py
# -*- coding: utf-8 -*- """ Created on Sat Aug 25 22:41:09 2018 frozen dir @author: yanhua """ import sys import os def app_path(): """Returns the base application path.""" if hasattr(sys, 'frozen'): # Handles PyInstaller return os.path.dirname(sys.executable) return os.path.dirname(__file__)
其中的app_path()函數(shù)返回一個程序的執(zhí)行路徑,為了方便我們將此文件放在項目文件的根目錄,通過這種方式建立了相對路徑的關(guān)系。
源代碼中使用路徑時,以app_path()的返回值作為基準路徑,其它路徑都是其相對路徑。以本文中使用的python項目打包為例,如下所示
import frozen_dir SETUP_DIR = frozen_dir.app_path() FONT_MSYH = matplotlib.font_manager.FontProperties( fname = SETUP_DIR + r'\data\fonts\msyh.ttf', size = 8) DIR_HELP_DOC = SETUP_DIR + r'\data\docs' DIR_HELP_VIDEO = SETUP_DIR + r'\data\videos'
通過凍結(jié)路徑,使用了基準目錄下的data目錄下的fonts, docs, videos。
主程序中也做了類似的調(diào)整,改變其設(shè)置路徑方法
import frozen_dir SETUP_DIR = frozen_dir.app_path()+r'\lib' sys.path.append(SETUP_DIR)
使用這樣的方法進行打包,打包后的可執(zhí)行程序就可以在其它機器上運行。
其它問題
由于操作系統(tǒng)和運行環(huán)境的不同,pyinstaller打包中還可能遇到很多其它問題,最后總結(jié)一些我在打包中遇到的其它坑:
1.權(quán)限問題
通常時在打包時出現(xiàn)的某些文件拒絕訪問或沒有權(quán)限執(zhí)行某些操作等。解決這個的方法一般有這幾個方面:
a)使用管理員權(quán)限運行cmd或其它命令行窗口
b)關(guān)閉殺毒軟件
c)使用完全權(quán)限的管理員賬戶
2.中文路徑
pyinstaller打包后的路徑使用中文沒有問題,不過為了減少打包時候出錯的可能,盡量將打包使用的資源文件和代碼文件路徑設(shè)置為英文。
3.打包后文件的大小
通常python打包為可執(zhí)行文件都會得到一個較大的包,這是無法避免的,但是我們還是可以通過一些方法來盡量精簡打包后的執(zhí)行程序:
a)在代碼中減少不必要的import,如from xxx import *
b)在精簡的運行環(huán)境(如原生python環(huán)境)下打包,缺什么包就下什么包,避免不必要的python包被打包入程序。尤其是anaconda這樣的集成環(huán)境下打包的結(jié)果會大很多。
c)使用UPX
到此這篇關(guān)于Pyinstaller 打包發(fā)布經(jīng)驗總結(jié)的文章就介紹到這了,更多相關(guān)Pyinstaller 打包發(fā)布內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
PyQt5實現(xiàn)讓QScrollArea支持鼠標拖動的操作方法
今天小編就為大家分享一篇PyQt5實現(xiàn)讓QScrollArea支持鼠標拖動的操作方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-06-06python+selenium 定位到元素,無法點擊的解決方法
今天小編就為大家分享一篇python+selenium 定位到元素,無法點擊的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-01-01利用插件和python實現(xiàn)Excel轉(zhuǎn)json的兩種辦法
轉(zhuǎn)換Excel表格到JSON格式有很多方法,下面這篇文章主要給大家介紹了關(guān)于利用插件和python實現(xiàn)Excel轉(zhuǎn)json的兩種辦法,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2023-11-11Python中paramiko模塊的基礎(chǔ)操作與排錯問題
python的ssh庫操作需要引入一個遠程控制的模塊——paramiko,可用于對遠程服務(wù)器進行命令或文件操作,這篇文章主要介紹了Python學習之paramiko模塊的基礎(chǔ)操作與排錯,需要的朋友可以參考下2022-09-09