如果将Python代码使用pyinstaller打包成exe文件时,只有一个大的exe文件,很多人可能遇到,在本机上就已经不能运行了,有的情况好些,在本机可以运行,发给别人就无法正常运行。本文本就是分析一下原因。
生成的exe文件,运行时自动释放到 C:\Users\"你当前用户"\AppData\Local\Temp\_MEIXXXXX,
如果你源代码中都使用绝对路径读的文件,那么你可能在本机上运行就是正常,但是如果分给别人,资源路径不对应就会出错了。
还有当你引用其它库的时候,库里面再读取文件一般是相对路径,例如
python实战项目词云生成器(wordcloud+jieba+pyinstaller打包)——词云生成软件【Pyinstaller打包问题解决】 - 程序员大本营
Pyinstaller打包jieba项目相关解决方案_浅零半泣的博客-CSDN博客_pyinstaller打包jieba
pyinstaller、jieba、wordcloud打包exe过程中遇到的问题总结一下_威震四海的博客-CSDN博客
使用结巴分词后程序打包失败_Purogram的博客-CSDN博客
如果一个一个去改源代码,就比较麻烦,最容易的方法就是把用到的库都放到你的要生成的.py文件同一目录下,并在.spec文件中加入他们,这样读取就没有问题了。见红字的。
# -*- mode: python -*-
import sys
import os.path as osp
sys.setrecursionlimit(25000000)
block_cipher = None
SETUP_DIR = 'D:\\working\\Python\\readHeadFileTraining\\NPL\\NPL_SanguoAndHamlet\\wordCloud'
a = Analysis(['wordcloudinterface.py'],
pathex=[SETUP_DIR],
binaries=[],
datas=[('data','data'),('wordcloud','wordcloud'),('jieba','jieba'),('cv2','cv2')],
hiddenimports=[],
hookspath=[],
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='wordcloudinterface',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=True,icon='demo.ico' )
另外,如果程序中读了文件,pyinstall也不会自动打包的。
比如我的程序读取了 data文件夹下的MSYH.tff字体文件,如果我已经将data打进包了,这时要这样改进代码
#生成资源文件目录访问路径 def resource_path(relative_path): if getattr(sys, 'frozen', False): #是否Bundle Resource base_path = sys._MEIPASS else: base_path = os.path.abspath(".") return os.path.join(base_path, relative_path)
myFontPath= resource_path("data/MSYH.TTC")
根据不同情况进行判断,保证本机在调试、及打包正常运行。
如果只用以下的声明
myFontPath= "data/MSYH.TTC"
那么在生成后的exe文件就肯定不好用了。除非你在.exe文件夹里也一个data文件夹,里面也有一个MSYH.TTC
如果只用以下的声明
myFontPath= "c:/test/data/MSYH.TTC"
那么在生成后的exe文件就只能在本机好用了。除非你发给别人的系统也有一样的路径文件夹里也一个data文件夹,里面也有一个MSYH.TTC才可以正常运行
所以原代码中用到相对路径要改,另外引用的库可能也会用到相对路径,也要修改,例如jieba,cv2,wordcloud等库,具体问题要具体分析,基本都是这个原因引起的
另外还输出这样一个Warning
[28224] WARNING: file already exists but should not: C:\Users\ADMINI~1\AppData\Local\Temp\_MEI282242\M2Crypto\_m2crypto.cp39-win_amd64.pyd
报错内容可能不同,但都是xxx已存在,问题的原因是pyinstaller打包时多打了一次,所以会报已经存在了。 这个解决方案就是把多余的去掉。 在自动生成的xxx.spec中,在 a 和 PYZ 中间添加如下代码,去掉多余依赖项
for d in a.datas:
if '_C.cp37-win_amd64' in d[0]:
a.datas.remove(d)
break
或者把不必要的包去了,#('M2Crypto','M2Crypto'),就是多增加的包,去了警告就没有了。也可以加入上面的代码进行过滤
# -*- mode: python ; coding: utf-8 -*-
import sys
import os.path as osp
sys.setrecursionlimit(5000000)
block_cipher = None
#SETUP_DIR 为当前工程的目录
print('为当前工程的目录',sys.path[0])
SETUP_DIR = r'D:\working\crackHuawei\new_hegui\compliance'
a = Analysis(['src\\reg.py', 'src\\read.py', 'src\\rsa.py'],
pathex=[SETUP_DIR],
binaries=[],
datas=[
('M2Crypto','M2Crypto'),\
('http','http')\
],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
for d in a.datas:
if '_m2crypto.cp39-win_amd64.pyd' in d[0]:
a.datas.remove(d)
break
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='reg',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
icon='demo.ico' )
上面的打包程序,在本机已经可以运行了,但是放到另外的机器上却运行不起来,后来发现是少了两个dll没打到包里去,于是把这两个dll增加到里面去改进代码如下:
# -*- mode: python ; coding: utf-8 -*-
import sys
import os.path as osp
sys.setrecursionlimit(5000000)
block_cipher = None
#SETUP_DIR 为当前工程的目录
SETUP_DIR = os.path.dirname(os.path.realpath('reg.spec'))
print('为当前工程的目录',SETUP_DIR)
a = Analysis(['src\\reg.py', 'src\\read.py', 'src\\rsa.py'],
pathex=[SETUP_DIR],
binaries=[],
datas=[
('libcrypto-1_1-x64.dll','./'),\
('libssl-1_1-x64.dll','./'),\
#('cryptolib','cryptolib'),\
#('M2Crypto','M2Crypto'),\
('http','http')\
],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
for d in a.datas:
if '_m2crypto.cp39-win_amd64.pyd' in d[0]:
a.datas.remove(d)
break
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='reg',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
icon='demo.ico' )
至于怎么发现这两个dll的,我是发现在不可以运行他们的机器上只要加上path=C:\ProgramData\Anaconda3\Library\bin
就说明所需要的dll在这个bin目录下,于是,将bin另外copy到与exe同一目录下,结果运行exe还是不行,但是我用我加了path=我的应用的目录 就可以运行了,说明python打包的exe是先把自己释放到tmp文件夹下,这时再运行tmp文件夹下的真实应用,这样,原来应目录中的dll就连接不上,但是在cmd中加了path就不一样了,这时去删除原exe文件夹下的dll文件,有些会不让删除,那么这些就是exe所需要的把他们打进包里即可。
另外增加uac_admin的方法也比较简单,如下图,
# -*- mode: python ; coding: utf-8 -*-
import sys
import os.path as osp
sys.setrecursionlimit(5000000)
block_cipher = None
#SETUP_DIR 为当前工程的目录
SETUP_DIR = os.path.dirname(os.path.realpath('gui.spec'))
print('为当前工程的目录',SETUP_DIR)
a = Analysis([
'gui.py',
],
pathex=[SETUP_DIR],
binaries=[],
datas=[
('env','env'),\
('utils','utils'),\
('ui','ui'),\
('res','res'),\
('module','module'),\
],
hiddenimports=[],
hookspath=[],
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='gui',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
#runtime_tmpdir=None,#这个参数会释放到C:\Users\ADMINI~1\AppData\Local\Temp\,但如果用户名有中文就会异常
runtime_tmpdir='C:\Windows\Temp',#这个参数会释放到C:\Windows\Temp\,但这时需要用户有管理员权限运行
console=True,
uac_admin=True,#这个参数会将uac_admin权限给exe
icon='icon2.ico'
)
2024-04-29 备注一下,最新的pyinstaller ==6.6.0时不用参数了
pyinstaller 你编写的.spec