pyinstaller打包原理,常见问题。

如果将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

 

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值