使用pyinstaller打包python程序并发布
简介
PyInstaller 将 Python 应用程序及其所有依赖项捆绑到单个软件包中。用户无需安装 Python 解释器或任何模块,即可运行打包后的应用程序。PyInstaller 支持 Python 3.8 及其更新版本,并能正确捆绑 numpy、matplotlib、PyQt、wxPython 等许多主流 Python 包。
PyInstaller 已经过 Windows、MacOS X 和 Linux 测试。不过,它并不是一个交叉编译器;要制作 Windows 应用程序就需要在 Windows 上运行 PyInstaller,要制作 Linux 应用程序就需要在 Linux 上运行它,依此类推。PyInstaller 已经成功地在 AIX、Solaris、FreeBSD 和 OpenBSD 上使用,但针对这些平台的测试并不是我们持续集成测试的一部分,开发团队也不能保证(这些平台的所有代码都来自外部贡献)PyInstaller 将能够在这些平台上运行,或得到持续支持。
- upx
UPX 是一款用于压缩可执行文件和库的免费工具。它适用于大多数操作系统,可用压缩大量可执行文件格式。
当 UPX 可用时,PyInstaller 会用它来单独压缩每个收集的二进制文件(可执行文件、共享库或 Python 扩展)以减小冻结应用程序(单文件夹捆绑的目录或单文件可执行文件)的整体大小。冻结应用程序的可执行文件本身并没有经过 UPX 压缩(不管是单文件夹模式还是单文件模式),因为其文件大小中的大部分都已经是由包含单独压缩文件的嵌入式压缩包构成。
PyInstaller 会在标准可执行路径(由 PATH 环境变量定义)或通过 –upx-dir 命令行选项指定的路径中查找 UPX。如果找到,就会自动使用。使用 –noupx 命令行选项可用完全禁用 UPX。
安装
- 使用以下命令安装
1
pip install pyinstaller
- 版本升级
1
pip install --upgrade pyinstaller
- 常用command option
1
2
3
4
5
6
7
8
9
10
11参数 说明
-F # 产生单个的可执行文件
-D # 产生一个目录(包含多个文件)作为可执行程序
-a # 不包含 Unicode 字符集支持
-d # debug 版本的可执行文件
-w # 指定程序运行时不显示命令行窗口(仅对 Windows 有效)
-c # 指定使用命令行窗口运行程序(仅对 Windows 有效)
-o # 指定 spec 文件的生成目录。如果没有指定,则默认使用当前目录来生成 spec 文件
-p # 设置 Python 导入模块的路径(和设置 PYTHONPATH 环境变量的作用相似)。也可使用路径分隔符(Windows 使用分号,Linux 使用冒号)来分隔多个路径
-n # 指定项目(产生的 spec)名字。如果省略该选项,那么第一个脚本的主文件名将作为 spec 的名字
发布
举例 1
1 | pyinstaller -F -w -i favicon.ico test.py |
举例 2(利用spec配置文件打包)
1 | # -*- mode: python ; coding: utf-8 -*- |
- 编写上述配置文件后,命名为test.spec,使用以下命令打包:
1
pyinstaller -D test.spec
应用
demo举例
- spec文件举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['ui_main.py'],
pathex=[],
binaries=[],
datas=[
('C:\\Users\\ad\\PycharmProjects\\pro\\src\\file','file')
],
hiddenimports=[],
hookspath=[],
hooksconfig={},
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='test',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon='test.ico'
)
扩展
- 可通过bat或sh脚本执行打包,举例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15@echo off
setlocal
REM 删除dist和build目录
if exist "dist" (
rmdir /s /q "dist"
)
if exist "build" (
rmdir /s /q "build"
)
REM 运行pyinstaller main_sis.spec进行打包
pyinstaller main_sis.spec
endlocal - sh
1
pyinstaller main_sis.spec
spec解析
详细如下
1 | # -*- mode: python ; coding: utf-8 -*- |
问题
1.pyinstaller 打包后无法通过反射访问py文件
- 解决方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53from crawlers.base import BaseCrawler
from crawlers.public.daili66 import Daili66Crawler
from crawlers.public.data5u import Data5UCrawler
from crawlers.public.docip import DocipCrawler
from crawlers.public.fatezero import FatezeroCrawler
from crawlers.public.geonodedaili import GeonodeCrawler
from crawlers.public.goubanjia import GoubanjiaCrawler
from crawlers.public.ihuan import IhuanCrawler
from crawlers.public.ip3366 import IP3366Crawler
from crawlers.public.iphai import IPHaiCrawler
from crawlers.public.jiangxianli import JiangxianliCrawler
from crawlers.public.kuaidaili import KuaidailiCrawler
from crawlers.public.seofangfa import SeoFangFaCrawler
from crawlers.public.taiyangdaili import TaiyangdailiCrawler
from crawlers.public.uqidata import UqidataCrawler
from crawlers.public.xiaoshudaili import XiaoShuCrawler
from crawlers.public.xicidaili import XicidailiCrawler
from crawlers.public.xiladaili import XiladailiCrawler
from crawlers.public.yqie import YqIeCrawler
from crawlers.public.zhandaye import ZhandayeCrawler
# 获取模块中所有的类
crawlers_cls = [
base,
Ip89Crawler,
Daili66Crawler,
Data5UCrawler,
DocipCrawler,
FatezeroCrawler,
GeonodeCrawler,
GoubanjiaCrawler,
IhuanCrawler,
IP3366Crawler,
IPHaiCrawler,
JiangxianliCrawler,
KuaidailiCrawler,
SeoFangFaCrawler,
TaiyangdailiCrawler,
UqidataCrawler,
XiaoShuCrawler,
XicidailiCrawler,
XiladailiCrawler,
YqIeCrawler,
ZhandayeCrawler
]
crawlers_cls = [cls for cls in crawlers_cls
if isinstance(cls, type) and issubclass(cls, BaseCrawler) and cls is not BaseCrawler
and not getattr(cls, 'ignore', False)]
# 调用
self.crawlers = [crawler_cls() for crawler_cls in self.crawlers_cls]
2.打包后无法访问指定数据
- 解决办法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def get_data_file(filename):
"""
获取数据文件的路径,无论是直接运行还是通过 PyInstaller 打包 抑或nuitka打包
:param filename
:return
"""
# pyinstaller 打包
if getattr(sys, 'frozen', False):
# 如果程序是“冷冻的”,即打包后的 exe
# nuitka 打包
if run.__compiled__:
basedir = os.getcwd()
else:
basedir = sys._MEIPASS
else:
# 如果程序是直接运行的,即没有打包
basedir = os.path.dirname(__file__)
return os.path.join(basedir, filename)