找回密码
 会员注册
查看: 34|回复: 0

python嵌入式打包,打包新姿势!打包速度比pyinstaller还快哦

[复制链接]

5

主题

0

回帖

16

积分

新手上路

积分
16
发表于 2024-9-7 17:28:24 | 显示全部楼层 |阅读模式
今天给大家介绍一种python程序的打包方式,使用python嵌入式解释器来打包程序。本打包姿势融合了较多本人的理解,使得其最终实现的效果优于常规的pyinstaller及nuitka。写在前面,本文充分借鉴了韦易笑大佬的文章,非常感谢大佬的分享。怎么样打包pyqt应用才是最佳方案?或者说pyqt怎样的发布方式最优?首先需要介绍,什么是嵌入式打包?嵌入式打包指的是使用python官方提供的pythonembeddable解释器,来打包一个独立的python环境。注意:本打包方法仅适用于python3.5版本及以上。示例程序接下来介绍本文所用的示例程序,为了简单,本文仅使用numpy生成数据,pandas处理数据,matplotlib绘图,pyqt5显示结果,都是非常常见的第三方依赖。importsysimportnumpyasnpimportpandasaspdfromPyQt5.QtWidgetsimportQApplication,QMainWindow,QVBoxLayout,QWidget,QLabelfrommatplotlib.backends.backend_qt5aggimportFigureCanvasQTAggasFigureCanvasfrommatplotlib.figureimportFigureclassMplCanvas(FigureCanvas):"""Ultimately,thisisaQWidget(aswellasaFigureCanvasAgg,etc.)."""def__init__(self,parent=None,width=5,height=4,dpi=100):fig=Figure(figsize=(width,height),dpi=dpi)self.axes=fig.add_subplot()#初始化父类super(MplCanvas,self).__init__(fig)classMainWindow(QMainWindow):def__init__(self):super(MainWindow,self).__init__()#创建MplCanvas实例作为窗口中的中央部件self.canvas=MplCanvas(self,width=5,height=4,dpi=100)#生成数据并绘制x_values=np.linspace(0,2*np.pi,num=200)y_values=np.sin(x_values)df=pd.DataFrame({'x':x_values,'sin(x)':y_values})self.canvas.axes.plot(x_values,y_values,label='SineFunction')#设置图表、坐标轴标签等self.canvas.axes.set_xlabel('Xaxis(x)')self.canvas.axes.set_ylabel('Yaxis(sin(x))')self.canvas.axes.set_title('y=sin(x)')self.canvas.axes.legend()#布局管理layout=QVBoxLayout()layout.addWidget(self.canvas)self.text=QLabel(str(df.info))layout.addWidget(self.text)container=QWidget()container.setLayout(layout)self.setCentralWidget(container)if__name__=='__main__':app=QApplication(sys.argv)main_win=MainWindow()main_win.show()sys.exit(app.exec_())程序运行结果如下,包含个曲线图以及一段pandas的dataframe信息。操作步骤接下来就不废话了,直接上操作步骤。1,下载pystand项目中的pystand程序,这是一个壳,用来调用python解释器运行代码。pystand项目只需要看这两个就行,一个是py38-x64,一个是py38,分别对应64位解释器和32位解释器,同时也对应了64位的pystand及32位的pystand,请注意这俩一定要对应上。不能用32位的pystand去运行64位的python解释器。2,本文以64位为例,下载PyStand-py38-x64.7z。下载到本地后,它应该长这样。解释一下,runtime文件夹里面放pythonembeddable解释器,这里面自带的是python3.8.10。site-packages里面放第三方库。pystand.exe是一个c语言写的壳,双击即可运行pystand.int文件中的代码。pystand.int中保存的是python代码,你可以理解为main.py文件,这里需要改为程序的入口,后面会详细介绍。你可以选择将runtime文件夹里面的内容删掉,自己去官网下载其他版本的pythonembeddable解释器,我这里以python3.10为例,去官网下载python-3.10.5-embed-amd64.zip,并替换runtime文件夹中的文件。ok,现在python解释器已经配置完毕。3,将你的项目所依赖的虚拟环境下的sitepackage文件夹下所有内容copy到pystand的sitepackage文件夹下,请注意,如果你的项目所依赖的虚拟环境并不是特别的干净,比如还有项目根本没用到的scipy库等,我建议你重新新建虚拟环境,安装好依赖后拷贝。4,在pystand目录下,新建script目录,将程序代码拷贝到里面去。并对代码入口做简要修改,注释掉if__name__=='__main__':,新建入口函数,名称随意,这里以start为例。defstart():app=QApplication(sys.argv)main_win=MainWindow()main_win.show()sys.exit(app.exec_())#if__name__=='__main__':#app=QApplication(sys.argv)#main_win=MainWindow()#main_win.show()#sys.exit(app.exec_())5,修改pystand.int中的代码,以文本编辑器打开该文件,直接清空内容,输入以下代码。这样就表示,从start函数启动程序。但是第4步我们将main.py文件扔到了scripts文件夹下,程序是不知道main.py文件在哪儿的,所以会报错:frommainimportstart.ModuleNotFoundError:nomodulenamed"main"frommainimportstartif__name__=="__main__":#运行入口函数start()所以我们需要告诉一下python,你需要去scripts目录找main模块,这里利用.pth文件。新建main.txt文本文件(名称随意),里面写上scripts,保存,修改后缀名为.pth,将该文件放在pystand.exe同级目录下。 tips:你要是觉得放在pystand.exe同级目录显得很乱,你可以将新建的main.txt放在其他地方,里面的路径也可以写其他相对路径,如./scripts,../scripts等,这一块就不赘述了,自己问chatai吧。6,双击pystand.exe,运行程序,可得到如期结果。到这儿,打包算初步完成。!如果不能如期运行,可以使用cmd命令运行pystand.exe,可以查看到报错信息。!如果需要自定义程序名字,需要同时修改pystand.exe及pystand.int两个文件,保持同名即可。!如果觉得保持同名很麻烦,想允许用户自己随便改名,那么请将pystand.int改名为_pystand_static.int即可。!如果需要自定义程序图标,请使用ResourceHacker更换图标,非常简单。!程序源代码是以源码形式放在script目录中的,建议使用nuitka批量将py文件转为pyd文件,提升运行速度的同时隐藏了源码。!如果有需要,也可以自己编译pystand,修改原代码后彻底去除int文件。nuitka转pyd命令:对文件:切换至scripts文件夹下,python-mnuitka--modulemain.py对文件夹:假设代码都放在app文件夹下。切换至scripts文件夹下,python-mnuitka--moduleapp--include-package=app打包到这里就算完成了。先简要总结下此打包方法的优缺点:优点:①熟练后打包速度非常快,只需要简单的复制粘贴即可。②不怕缺包漏包,嵌入式解释器自带了所有的标准库(在那个zip文件里,通过python310._pth导入),同时咱们拷贝了sitepackge文件夹,不存在pyinstaller及nuitka自动分析依赖所导致的缺文件问题。③exe外壳用的是pystand,该项目已存续两年,用户很多,因此打包后报毒风险较低。④软件启动速度,运行速度优于pyinstaller,与nuitka打包的程序也可以简单比比(当然比不上nuitka全编译)。缺点:sitepackage文件夹体积过大。下面的步骤在于解决sitepackage体积过大的问题。接下来就是本文打包姿势所表演的时候了。7,查看打包体积,足足有338M,这太大了。优化第一步,删除不需要的固有第三方库,请删除sitepackage文件夹下的pip,pip-info,wheel,wheel-info,setuptools,setuptools-info,合计6个文件夹,这大概是15M。删除rruntime文件夹里的get-pip.py文件,这文件居然有2M,是用来安装pip的,不需要它。优化第二步,使用神秘脚本删除程序运行不需要的第三方库文件。请到下面的链接获取,如果觉得好用,请大力宣传哦!打包瘦身脚本请仔细阅读该脚本使用说明,本文仅展示瘦身效果。瘦身前:瘦身后:从338M减小到168M,减小了170M,50%的瘦身效果,删除的文件以原本的目录结构保存在site-packages_new文件夹中,删除的文件清单保存在site-packages_文件移动清单.txt中,可随时查看。运行程序,程序运行无误,瘦身完美!如果运行失败,请使用cmd命令运行pystand.exe程序,根据错误提示信息从文件移动清单中寻找缺失到的文件,还原即可。本部分内容请详细阅读脚本使用说明。优化第三步,使用upx对打包后的文件夹中的dll,pyd,exe文件进行压缩,进一步压缩空间。tips:使用upx后,并不能减小最终7z压缩包的体积,同时会增大程序运行时占用内存(大概就是翻倍)及增加程序启动时间。frompathlibimportPathimportosdir_path=Path("E:\\pystand\\site-packages")upx_path=Path('D:\\ProgramData\\anaconda3\\Scripts\\upx.exe')command=f"{upx_path}--compress-icons=0--lzma--strip-loadconf"upack=f"{upx_path}-d"exclude_files=["qwindows.dll","qwindowsvistastyle.dll","arrow.dll"]extensions_to_find=('.pyd','.dll','.exe')defcheck_file(file_pathath)->bool:forfileinexclude_files:iffileinstr(file_path):returnFalsereturnTrue#遍历目录及其子目录,找到指定后缀名的文件forextinextensions_to_find:files=list(dir_path.glob("**/*{}".format(ext)))forfile_pathinfiles:ifcheck_file(file_path):upx_command=command+str(file_path)os.system(upx_command)print("upx操作结束")压缩结束后,查看打包体积,103M,相比一开始的338M,此时瘦身效果已达70%。此时基本已经实现了较大的瘦身效果,下面的步骤收益不大了,感兴趣请继续往下看。优化第四步,使用批处理命令命令依次实现以下3个目标操作,对sitepackage文件夹,①将.py文件编译为pyc文件,②删除py文件,③删除__pycache__文件夹。SET"SOURCE_DIR=E:\pystand\site-packages"SET"PYTHON_PATH=E:\pystand\runtime\python.exe"setlocalenabledelayedexpansion"%PYTHON_PATH%"-mcompileall-b"%SOURCE_DIR%"FOR/R"%SOURCE_DIR%"%%iIN(*.py)DO(echoDeletingfile:%%idel"%%i")echoAll.pyfileshavebeendeleted.for/r"%SOURCE_DIR%"%%din(__pycache__)do(echoRemovingdirectory:%%drmdir/s/q"%%d")echoAll__pycache__directorieshavebeenremoved.pause操作完成后,体积减小到84M,此时瘦身效果达75%。优化第五步,这一步就不演示了,可以使用7z.sfx/BoxedAppPacker2020将程序制作为单文件程序,可以使用innosetup将程序制作为安装包,或直接7z压缩发给用户。使用7z压缩后,体积大概为36M左右。制作为单文件程序体积大概为36M,这个时候就可以跟pyinstaller的单文件模式对比一下了,绝对是秒杀。但是我绝对不推荐将程序打包发布为单文件程序,你看看你电脑上哪个大型软件是一个文件呢?单文件制作工具获取链接:https://pan.baidu.com/s/1o9jE9SJ9ecBzBT728-tQtA?pwd=vecq提取码:vecq复制这段内容后打开百度网盘手机App,操作更方便哦群友反馈加密源码按照本文所推荐的打包方式,源码其实是放在script文件夹中,以python源码的形式存放的。现在我们来简单加密一下,不让别人随随便看见。原理就是把py文件编译为pyd文件,用于替代py文件。注意:这个只能转换py文件,其余的资源文件或二进制文件保留原路径即可。资源文件路径我建议代码里通过工作路径+相对路径拼接这里可能有几种情况:①只有一个py文件,比如本文的main.py文件,该文件目前是放在scripts文件夹下,使用cd命令切换至script文件夹。然后使用nuitka命令命令如下:python-mnuitka--modulemain.py完事后会生成main.pyd文件,将原来的main.py及__pycache__文件夹(这个文件夹如果有一定要删,里面是pyc文件,可以完美还原源码)删掉。②只有一个代码文件夹,文件夹包含子文件夹或代码文件,名字假设为app。使用cd命令切换至script文件夹。然后使用nuitka命令命令如下:python-mnuitka--moduleapp--include-package=app完事后会生成app.pyd文件,将原来的app文件夹里面的源码及__pycache__文件夹(这个文件夹如果有一要删,里面是pyc文件,可以完美还原源码)删掉。③多个同级目录代码文件及代码文件夹那就只能重复使用上述①②情况下的命令编译,生成多个pyd(模块)文件。pyd文件比pyc文件安全的多,它类似于dll文件,一般人很难破解,想逆向还原源码,那更是不可能。但是pyd文件作为一个依赖模块,是可以直接通过dir函数查看其暴露的接口的,所以请将重要变量如key什么的匿名化(不能被import,dir出来)。这里贴一下nuitka维护者关于pyd文件安全性的回复当然pyd文件在专业的人手里,那还是有破解的可能,类似于dll文件逆向。如果还想继续加密,可以参考本文开头所引用的韦易笑大佬的文章中的代码加密办法。本人不擅长此道。常见问题大部分问题都可以在pystand项目issues页找到答案,下面简要贴出几个常见问题及解决方案。1,multiprocessing相关代码打包后运行,重复开启多个窗口。解决方案:ifnothasattr(sys,'frozen'):sys.frozen=Truemultiprocessing.freeze_support()2,目录含中文,报错noQTplatformplugin解决方案:pyside同理importsys,osimportos.pathos.environ['QT_QPA_PLATFORM_PLUGIN_PATH']=r'.\site-packages\PyQt5\Qt5\plugins'####这一行是新增的。用的是相对路径。fromPyQt5.QtCoreimport*fromPyQt5.QtWidgetsimport*app=QApplication([])3,打包含tkinter的程序官方提供的嵌入式解释器并不包含pip工具,以及tkinter模块,下面的步骤是怎么补上这个tkinter模块。注:本方法目的在于后续可以复用,所以并不按照官方的目录结构来。①复制tkinter模块。从已经安装了tkinter模块的相同 python解释器环境中复制,该模块通常位于解释器的Lib文件夹下,将tkinter文件夹复制到pystand/runtime/lib文件夹下,注意,这里需要新建一个lib文件夹。②复制tcl资源文件。复制tcl文件夹里面所有的文件,到pystand/runtime/lib文件夹下,tcl文件夹通常位于解释器的同级目录。③复制二进制模块。复制_tkinter.pyd,tcl86t.dll,tk86t.dll三个文件到pystand/runtime/lib文件夹下,这三个文件通常位于解释器的DLLs文件夹下。④修改路径。修改runtime文件夹里面的python310._pth文件,增加一行./lib。注意,这里有个点,代表同级目录的lib文件夹。现在可以运行程序了。如果后续还需要打包tkinter模块。直接复制这里弄好的lib文件夹跟._pth文件到runtime文件夹下即可实现复用。视频讲解python嵌入式打包_哔哩哔哩_bilibili总结本文实际上是对嵌入式打包这种打包方式的进一步完善,原来流行的嵌入式打包方法,其打包后sitepackage文件夹体积过于庞大,本文结合了瘦身工具,使得打包后的体积与pyinstaller/nuitka相比也毫不逊色,同时拥有了更强的性能与更快的启动速度。熟练后,打包速度也非常快。下面简单总结了几种打包方式的优缺点,欢迎讨论。总算写完了,下一篇文章打算讲讲嵌入式打包相对于pyinstaller打包,nuitka打包的优点,这里先卖个关子。只能用遥遥领先来形容。附录有打包相关问题咨询的可以加群,群里面大佬甚多注意:新群友进群后,按下面格式做自我介绍比如:版面:Python与模具(您的知乎号,B站,或者公众号,可以为无,方便交流)行业:营销,模具,广告模块:Sanic,PyQt,爬虫,后端,全栈
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-11 06:04 , Processed in 0.436166 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表