Pyinstaller进阶之构建管理大杀器-SPEC文件

用过 python 打包生成 Windows 可执行程序的同学,大概率都用过大名鼎鼎的 pyinstaller 库吧。

pyinstaller 如果想最终生成的可执行程序,包括只限定的 python 指定模块或者要额外的静态资源文件,写执行指令一般都很长的。

shell 复制代码
pyinstaller --noconfirm --log-level=WARN \
    --onefile --nowindow \
    --add-data="README:." \
    --add-data="image1.png:img" \
    --add-binary="libfoo.so:lib" \
    --hidden-import=secret1 \
    --hidden-import=secret2 \
    --upx-dir=/usr/local/share

而有没有一种方式可以更好地进行 pyinstaller 的构建管理呢?看 官网文档:Using Spec Files --- PyInstaller 6.17.0 documentation,那就是使用 spec 文件进行构建管理。

PS:如果对 pyinstaller 基础使用不太了解,也可以看看小编的往期文章。 Pyinstaller - Python桌面应用打包的首选工具 - 掘金

1. spec文件的构成和使用

Q1:怎么使用 spec 文件呢?很简单:执行 pyinstaller 指令时带上对应的 spec 文件即可。

shell 复制代码
pyinstaller --clean --noconfirm xxx.spec

Q2:spec 文件是怎么做到有效构建和管理模块和资源文件的呢?

主要通过下面几个核心的配置步骤:Analysis-分析依赖 → PYZ-打包模块 → EXE-生成exe → [COLLECT-收集文件]

💡温馨提示:单文件模式下,不需要额外进行第四步骤收集文件。

下面具体展开进行介绍。


2. Analysis 类

2.1 作用场景

依赖分析器,这是打包过程的核心步骤。它会:

  • 分析你的Python脚本
  • 递归查找所有导入的模块
  • 收集所有需要的文件(Python模块、扩展模块、数据文件等)

✍️ 开头说的管理模块和数据文件,就主要在这里进行配置的。

2.2 参数配置详解

这里我用的比较多的就 excludeshiddenimportsdatas,其他默认填写或按照模板即可。

python 复制代码
a = Analysis(
    # ============ 必需参数 ============
    scripts=['main.py'],  # 主脚本列表

    # ============ 路径相关 ============
    pathex=['/path/to/project', '/another/path'],  # 模块搜索路径
    hookspath=['custom_hooks'],  # 自定义hook文件路径
    runtime_hooks=['runtime_hooks/hook-qt.py'],  # 运行时hook脚本

    # ============ 模块处理 ============
    excludes=[                          # 排除的模块(减少包体积)
        'tkinter',
        'unittest',
        'pydoc',
        'pdb',
        'matplotlib'
    ],
    hiddenimports=[                     # 隐藏导入(动态导入的模块)
        'PIL._imaging',
        'sklearn.utils._weight_vector',
        'pandas._libs.tslibs.timedeltas'
    ],
    win_no_prefer_redirects=False,      # Windows专用:DLL重定向
    win_private_assemblies=False,       # Windows专用:私有程序集

    # ============ 文件收集 ============
    datas=[                            # 非Python文件(配置文件、图片等)
        ('config.json', '.'),          # (源文件, 目标目录)
        ('images/*.png', 'images'),
        ('data/*.csv', 'data')
    ],
    binaries=[                         # 二进制文件(.so, .dll等)
        ('/usr/lib/libssl.so', 'lib'), # (源文件, 目标目录)
        ('C:/Windows/System32/msvcp140.dll', '.')
    ],

    # ============ 高级选项 ============
    cipher=block_cipher,               # 加密字节码的密钥
    noarchive=False,                   # True=不解压到临时目录
    optimize=0,                        # 字节码优化级别:-1, 0, 1, 2
    upx=False,                         # 是否用UPX压缩(已弃用,在EXE中设置)
    upx_exclude=[],                    # 排除UPX压缩的文件
    strip=False,                       # 是否去除符号信息
    prefetch=False,                    # 预取模式(实验性)
    name=None,                         # 分析名称(用于多程序打包)
)

2.3 实际示例

python 复制代码
# 复杂的Analysis配置
block_cipher = pyi_crypto.PyiBlockCipher(key='my-secret-key')

a = Analysis(
    ['app_main.py', 'helper.py'],  # 多个入口文件
    pathex=[os.getcwd(), '../shared_libs'],
    binaries=[
        ('lib/ffmpeg.exe', 'bin'),
        ('lib/iconv.dll', 'bin')
    ],
    datas=[
        ('ui/*.ui', 'ui'),           # Qt Designer文件
        ('locales/*.qm', 'locales'), # 翻译文件
        ('*.ini', '.'),              # 配置文件
        ('docs/*.md', 'docs')
    ],
    hiddenimports=[
        'win32timezone',             # pywin32相关
        'sqlalchemy.sql.default_comparator',
        'pkg_resources.py2_warn'
    ],
    hookspath=['hooks'],             # 自定义hook目录
    runtime_hooks=['hooks/runtime/rthook_pyside6.py'],
    excludes=['scipy', 'numpy.testing'],
    cipher=block_cipher,
    optimize=1                       # 字节码优化
)

3. EXE 类

3.1 作用场景

可执行文件构建器,将分析结果打包成最终的可执行文件。

3.2 参数配置详解

python 复制代码
exe = EXE(
    # ============ 必需参数 ============
    pyz,                # PYZ对象(包含所有Python模块)
    a.scripts,          # Analysis的scripts结果
    a.binaries,         # Analysis的binaries结果
    a.zipfiles,         # Analysis的zipfiles结果
    a.datas,            # Analysis的datas结果

    # ============ 输出配置 ============
    name='MyApp',       # 输出文件名(不加.exe)
    debug=False,        # True=包含调试信息
    strip=False,        # True=去除符号信息(减小体积)
    upx=True,           # True=使用UPX压缩(需安装UPX)

    # ============ 控制台/窗口设置 ============
    console=True,       # True=控制台程序,False=窗口程序(Windows)
    disable_windowed_traceback=False,  # 禁用窗口程序错误跟踪
    argv_emulation=False,  # macOS: 模拟命令行参数

    # ============ 图标和元数据 ============
    icon='app.ico',     # 程序图标(Windows: .ico, macOS: .icns)

    # ============ 运行时行为 ============
    bootloader_ignore_signals=False,  # 忽略信号(Unix)
    runtime_tmpdir=None,  # 临时目录路径

    # ============ 重定向设置 ============
    exclude_binaries=False,  # True=不包含二进制依赖
    code_signing_identity=None,  # macOS代码签名标识

    # ============ 加密和优化 ============
    entitlements_file=None,  # macOS授权文件
    embed_manifest=True,     # Windows: 嵌入manifest
    target_arch=None,        # 目标架构:'x86', 'x64', 'arm64'

    # ============ 其他选项 ============
    ascii=False,        # 弃用
    uac_admin=False,    # Windows: 以管理员运行
    uac_uiaccess=False, # Windows: UI访问权限

    # ============ 调试相关 ============
    bootloader_ignore_signals=False,
    strict_arch=False,
)

3.3 实际示例

这里还真没怎么差异化使用过,一般都是配置 win 平台。

python 复制代码
# 不同平台配置示例
if sys.platform == 'win32':
    exe = EXE(
        pyz,
        a.scripts,
        a.binaries,
        a.zipfiles,
        a.datas,
        name='MyApp',
        debug=False,
        strip=False,
        upx=True,
        console=False,  # 无控制台窗口
        icon='app.ico',
        uac_admin=True,  # 需要管理员权限
        embed_manifest=True,
        version='version_info.txt'  # Windows版本资源
    )
elif sys.platform == 'darwin':
    exe = EXE(
        pyz,
        a.scripts,
        a.binaries,
        a.zipfiles,
        a.datas,
        name='MyApp',
        debug=False,
        strip=True,
        upx=False,
        console=False,
        icon='app.icns',
        entitlements_file='app.entitlements',
        code_signing_identity='Developer ID Application: Your Name (XXXXXX)'
    )
else:  # Linux
    exe = EXE(
        pyz,
        a.scripts,
        a.binaries,
        a.zipfiles,
        a.datas,
        name='myapp',
        debug=False,
        strip=True,
        upx=True,
        console=True  # Linux通常用控制台
    )

4. todo-list应用完整文件示例

以最近我在撰写的 todo-list 应用的打包配置文件为例,这个主要是 AI 编写的,我就改了几行代码而已。

python 复制代码
# -*- mode: python ; coding: utf-8 -*-

import sys
from pathlib import Path

# 获取项目根目录
project_root = Path(SPECPATH).parent
frontend_dir = project_root / 'frontend'
data_dir = project_root / 'data'

# 收集前端文件
frontend_files = [
 ('frontend/index.html', 'frontend/'),
 ('frontend/css/*', 'frontend/css'),
 ('frontend/js/*', 'frontend/js')
]

# 收集数据文件
data_files = []
if data_dir.exists():
    for item in data_dir.rglob('*'):
        if item.is_file():
            relative_path = item.relative_to(data_dir)
            data_files.append((str(item), 'data'))

block_cipher = None

a = Analysis(
    ['run.py'],
    pathex=[str(project_root), str(project_root / 'backend'), str(project_root / 'TodoList' / 'backend')],
    binaries=[],
    datas=frontend_files + data_files,
    hiddenimports=[
        'webview',
        'webview.platforms',
        'webview.platforms.cef',
        'webview.platforms.edgechromium',
        'webview.platforms.mshtml',
        'webview.platforms.gtk',
        'webview.platforms.cocoa',
        'webview.platforms.qt',
        'sqlite3',
        'json',
        'threading',
        'datetime',
        'http.server',
        'socketserver',
        'urllib.parse',
        'pathlib',
        'os',
        'sys',
        'shutil',
        'subprocess',
        're',
        'uuid',
        'hashlib',
        'base64',
        'html',
        'webbrowser',
        'tkinter',
        'tkinter.messagebox',
        'tkinter.filedialog',
        'tkinter.simpledialog',
        # 后端模块
        'database.operations',
        'database.models',
        'api.todo_api'
    ],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[
        'matplotlib',
        'numpy',
        'scipy',
        'pandas',
        'PIL',
        'cv2',
        'PyQt6'
    ],
    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='TodoList',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=False,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
    icon='todo_icon.ico' if Path('todo_icon.ico').exists() else None,
    version='version_info.txt' if Path('version_info.txt').exists() else None
)

示例上可以看出实际上,spec 文件执行时是可以识别 python 代码的。

5. 补充说明

5.1 针对特定库的配置

python 复制代码
# 打包PyQt6/PySide6应用
a = Analysis(
    ['app.py'],
    hiddenimports=[
        'PySide6.QtXml',
        'PySide6.QtNetwork'
    ],
    binaries=[
        ('/path/to/qt/plugins/platforms/*', 'platforms')
    ],
    datas=[
        ('/path/to/qt/translations/*', 'translations')
    ]
)

# 打包机器学习应用(排除大型库)
a = Analysis(
    ['ml_app.py'],
    excludes=[
        'torch',  # 太大,让用户自己安装
        'tensorflow',
        'jax'
    ],
    hiddenimports=[
        'sklearn.utils._cython_blas',
        'numpy.core._multiarray_umath'
    ]
)

5.2 调试技巧

python 复制代码
# 调试模式配置
exe = EXE(
    # ...
    debug=True,      # 包含调试信息
    strip=False,     # 不去除符号
    upx=False,       # 不压缩(便于调试)
    console=True     # 显示控制台看错误
)

# 运行时查看缺失模块
import sys
if hasattr(sys, '_MEIPASS'):
    print(f"临时目录: {sys._MEIPASS}")
    print(f"模块路径: {sys.path}")

5.3 其他说明

除了用于 pyinstaller 的打包外,spec 文件也在其他场景有相关使用的:

  1. RPM包管理:Linux系统软件打包

  2. 其他构建工具:作为配置文件使用

另外有类似作用的文件还有:setup.cfgpyproject.toml


好啦,今天的分享就到这里了,感谢阅读,如果觉得文章对你有用的话,欢迎三连、欢迎关注!

相关推荐
一招定胜负2 小时前
杂记:cv2.imshow显示中文乱码解决过程
python·opencv
爱吃山竹的大肚肚2 小时前
在Java中,从List A中找出List B没有的数据(即求差集)
开发语言·windows·python
伯明翰java2 小时前
【无标题】springboot项目yml中使用中文注释报错的解决方法
java·spring boot·后端
ihgry2 小时前
java并发编程(juc理论篇)
后端
weixin_462446232 小时前
【原创实践】Python 将 Markdown 文件转换为 Word(docx)完整实现
开发语言·python·word
码界奇点2 小时前
基于Spring Boot和Vue.js的视频点播管理系统设计与实现
java·vue.js·spring boot·后端·spring·毕业设计·源代码管理
程序员根根2 小时前
MySQL 事务全解析:从 ACID 特性到实战落地(部门 - 员工场景)
数据库·后端
智航GIS2 小时前
ArcGIS大师之路500技---048计算流水号
python·arcgis
yongche_shi2 小时前
第九十九篇:Python在其他领域的应用:游戏开发、物联网、AIoT简介
开发语言·python·物联网·游戏开发·aiot