PyInstaller打包python腳本的一些心得
因為在公司經常要幫同事做一個從excel表格中提取出需要的內容的重復工作,比較繁瑣還容易出錯;於是就想着要寫個程序,但是同事又不可能在電腦上也裝上python以及相關的包依賴(別人一看就覺得太麻煩而且太冗余),於是就想着將寫好的python腳本打包成exe,直接雙擊使用,方便快捷。
說干就干,先是花點時間寫完了腳本;然后搜索了相關的關鍵詞,找到了py2exe、PyInstaller、cx_Freeze等工具,最后確定使用PyInstaller。
使用PyInstaller有幾個原因:
- PyInstaller現在仍然在更新
- PyInstaller使用方法簡單,py2exe比較繁瑣
- PyInstaller網上教程比較多
安裝PyInstaller
推薦使用pip安裝
pip install pyinstaller -i https://pypi.douban.com/simple
后面加的-i https://pypi.douban.com/simple
是使用豆瓣的源鏡像,在天朝速度會快很多;如果你擔心安全問題或者網速夠快,可以不加,使用官方的源。
安裝完后,直接
pyinstaller
usage: pyinstaller-script.py [-h] [-v] [-D] [-F] [--specpath DIR] [-n NAME] [-p DIR] [--hidden-import MODULENAME] [--additional-hooks-dir HOOKSPATH] [--runtime-hook RUNTIME_HOOKS] [--exclude-module EXCLUDES] [--key KEY] [-d] [-s] [--noupx] [-c] [-w] [-i <FILE.ico or FILE.exe,ID or FILE.icns>] [--version-file FILE] [-m <FILE or XML>] [-r RESOURCE] [--uac-admin] [--uac-uiaccess] [--win-private-assemblies] [--win-no-prefer-redirects] [--osx-bundle-identifier BUNDLE_IDENTIFIER] [--distpath DIR] [--workpath WORKPATH] [-y] [--upx-dir UPX_DIR] [-a] [--clean] [--log-level LEVEL] [--upx UPX] scriptname [scriptname ...] pyinstaller-script.py: error: the following arguments are required: scriptname
可以看到PyInstaller的信息,說明安裝完成,可以使用了;詳細幫助可以pyinstaller -h
查看。
使用PyInstaller
PyInstaller的使用非常簡單:
# 打包成一個文件
pyinstaller -F test.py
# 打包成文件夾(默認)
pyinstaller test.py
因為要精簡到底,所以我選擇打包成一個文件,打包完成后,打開dist
文件夾,里面的那個exe文件就是打包好的程序,運行測試一下是否打包成功。
PyInstaller本身也是有很多選項的。這里挑幾個主要的說明一下:
-D, --one-dir
打包成一個文件夾,默認-F, --one-file
打包成一個exe文件-p DIR, --paths DIR
添加路徑,一般用來添加程序所用到的包的所在位置-c, --console, --nowindowed
提供程序視窗,程序有輸入輸出的界面,默認-w, --windowed, --noconsole
無視窗,程序后台運行-i <FILE.ico or FILE.exe,ID or FILE.icns>, --icon <FILE.ico or FILE.exe,ID or FILE.icns>
添加icon圖標
openpyxl的一個錯誤
程序打包之后一定要測試一下是否能成功運行,不然會在同事面前出糗,另外還需要用另一台電腦測試一下。
當我試着運行程序時,發生了報錯:
Traceback (most recent call last):
File "test.py", line 72, in <module>
File "site-packages\pandas\core\frame.py", line 1414, in to_excel
File "site-packages\pandas\io\excel.py", line 609, in __new__
File "site-packages\pandas\io\excel.py", line 59, in get_writer
AttributeError: module 'openpyxl' has no attribute '__version__'
Failed to execute script test
於是就上網查找原因,一開始是去打包生成的build
文件夾下面,查看一個名為warntest.txt
的文件(這里是warn[yourscriptname].txt),發現有很多module都是miss,沒有加載到。
missing module named 'win32com.gen_py' - imported by win32com, c:\python35\lib\site-packages\PyInstaller\loader\rthooks\pyi_rth_win32comgenpy.py
missing module named sys.exc_info - imported by sys, openpyxl.reader.excel, win32com.server.dispatcher
missing module named pywintypes.IIDType - imported by pywintypes, win32com.client.dynamic
missing module named win32com.client._get_good_object_ - imported by win32com.client, win32com.client.util
...
於是就想着會不會是包的路徑沒有加載,嘗試着使用-p path
去加載python下面儲存package的目錄,結果,重新打包一次仍然還是同樣的報錯。
於是我重新通過AttributeError: module 'openpyxl' has no attribute '__version__'
去搜索結果,發現有人遇到同樣的bug,原因是使用了Pandas,但是pyinstaller在pandas引用的‘openpyxl’包中,無法讀取版本信息。*(一般使用Python處理科學數據都會使用到Pandas,我是處理excel文件的腳本,當然會用到openpyxl來讀寫excel)*
解決的辦法是在PyInstaller的hook
文件夾中添加‘openpyxl’的一個讀取版本信息的hook。這個hook文件是在PyInstaller的Github issue上找到的。於是添加了這個hook,再重新打包,然后運行測試,終於成功了。
關於前面說到的warn[youscriptname].txt
文件,有一種說法是如果你在其中沒有找到你所用到的包,那么里面的錯誤信息一般可以忽略,anyway,我是直接忽略掉的。
添加版本信息
辛辛苦苦寫了個程序,當然希望給程序簽個名;PyInstaller是可以添加你自己的個人版本信息的,詳細可以參考《Creating an Executable from a Python Script》,寫的非常詳細,依照相同的格式修改version.txt
即可。但不知什么原因,我試了好幾次都沒有成功。如果以后找到原因,我再更新這篇文章。
關於32位和64位
這個算是后續劇情了。當把程序發給一位同事時,沒想到同事的win系統是32位的,而我一直使用的都是64位的系統和64位的程序,而PyInstaller好像沒有能選擇生成32位/64位exe的選項;也算是PyInstaller的一個缺點吧。最后我是安裝了32位的Python以及依賴的庫重新生成32位的exe才解決這個問題;所以以后也許生成32位的程序兼容性更好。