目录
一、背景介绍
二、项目场景
三、准备工作
3.1 创建配置文件
3.2 创建依赖文件
四、代码修改
4.1 添加配置读取功能
4.2 完整代码示例
五、PyInstaller打包步骤
5.1 安装PyInstaller
5.2 打包命令
5.3 打包输出
5.4 运行可执行文件
六、跨平台打包注意事项
6.1 重要原则
6.2 打包环境要求
6.3 配置文件处理
七、完整打包脚本示例
总结
一、背景介绍
在实际项目中,我们经常需要将Python脚本部署到没有Python环境的服务器上运行。传统的做法是在目标机器上安装Python和所有依赖包,但这会带来以下问题:
1. **环境配置复杂**:需要手动安装Python、pip以及各种依赖包
2. **版本冲突**:不同项目可能依赖不同版本的包
3. **部署不便**:每次部署都需要重复配置环境
4. **跨平台问题**:Windows和Linux环境不兼容
本文将介绍如何使用PyInstaller将Python脚本打包成可执行文件,实现"一次打包,到处运行"的目标。
二、项目场景
PyInstaller官方文档:https://pyinstaller.org/
本文以一个实际的业务场景为例:批量处理Excel文件,调用Dify AI接口进行意图识别,并将结果写回Excel。
**原始脚本特点**:
- 依赖多个第三方库:`pandas`、`openpyxl`、`requests`
- 包含硬编码的配置信息(API地址、密钥等)
- 需要在Linux服务器上运行
**目标**:
- 打包成独立的可执行文件
- 配置信息外部化,便于修改
- 无需在目标机器安装Python环境
三、准备工作
3.1 创建配置文件
首先,我们需要将硬编码的配置信息提取到外部配置文件中。创建 `config.yaml`:
DIFY_HOST: "http://192.168.0.106" API_KEY: "app-xxxx" EXCEL: "test.xlsx" START_ROW: 1 END_ROW: 3 MAX_INTENTS: 5**配置文件优势**:
- 修改配置无需改动代码
- 不同环境可以使用不同配置
- 敏感信息(如API密钥)可以单独管理
3.2 创建依赖文件
创建 `requirements.txt`,列出所有依赖包:
pandas==2.2.3 openpyxl==3.1.5 requests==2.32.3 PyYAML==6.0.2**说明**:
- 建议指定版本号,避免版本冲突
- 打包时PyInstaller会自动分析并打包实际使用的依赖
四、代码修改
4.1 添加配置读取功能
修改Python脚本,添加配置读取逻辑:
import os import yaml # ========== 配置读取 ========== BASE_DIR = os.path.dirname(os.path.abspath(__file__)) CONFIG_PATH = os.path.join(BASE_DIR, "config.yaml") def load_config(): try: with open(CONFIG_PATH, "r", encoding="utf-8") as f: return yaml.safe_load(f) or {} except FileNotFoundError: print("⚠️ 未找到 config.yaml,使用默认配置") return {} cfg = load_config() DIFY_HOST = cfg.get("DIFY_HOST", "http://192.168.0.106") API_KEY = cfg.get("API_KEY", "app-XlQOSXdpzlRtBHt7nbHAIQ1e") EXCEL = cfg.get("EXCEL", "test.xlsx") START_ROW = int(cfg.get("START_ROW", 1)) END_ROW = int(cfg.get("END_ROW", 3)) MAX_INTENTS = int(cfg.get("MAX_INTENTS", 5)) # ==========================**关键点**:
- 使用 `os.path.dirname(os.path.abspath(__file__))` 获取脚本所在目录
- 配置文件与脚本放在同一目录
- 提供默认值,增强容错性
4.2 完整代码示例
"""Author: siroushengLastEditors: siroushengLastEditTime: 2025-xx-xx""" import os import pandas as pd import requests import time import yaml from openpyxl import load_workbook # ========== 配置读取 ========== BASE_DIR = os.path.dirname(os.path.abspath(__file__)) CONFIG_PATH = os.path.join(BASE_DIR, "config.yaml") def load_config(): try: with open(CONFIG_PATH, "r", encoding="utf-8") as f: return yaml.safe_load(f) or {} except FileNotFoundError: print("⚠️ 未找到 config.yaml,使用默认配置") return {} cfg = load_config() DIFY_HOST = cfg.get("DIFY_HOST", "http://192.168.0.106") API_KEY = cfg.get("API_KEY", "app-XlQOSXdpzlRtBHt7nbHAIQ1e") EXCEL = cfg.get("EXCEL", "test.xlsx") START_ROW = int(cfg.get("START_ROW", 1)) END_ROW = int(cfg.get("END_ROW", 3)) MAX_INTENTS = int(cfg.get("MAX_INTENTS", 5)) # ========================== # ... 其余业务逻辑代码 ...五、PyInstaller打包步骤
5.1 安装PyInstaller
在打包机器上安装PyInstaller:
pip install pyinstaller**注意**:如果打包机器上已有所需依赖包,无需重新安装。PyInstaller只会打包脚本实际导入的模块。
5.2 打包命令
**Linux环境打包**:
pyinstaller -F -n intention --add-data "config.yaml:." intention.py**Windows环境打包**(仅用于Windows部署):
pyinstaller -F -n intention --add-data "config.yaml;." intention.py**参数说明**:
- `-F`:生成单个可执行文件(onefile模式)
- `-n intention`:指定生成的可执行文件名称
- `--add-data "config.yaml:."`:将配置文件打包进可执行文件(Linux用冒号`:`,Windows用分号`;`)
- `intention.py`:要打包的Python脚本
5.3 打包输出
打包完成后,会在当前目录生成:
- `dist/intention`(Linux)或 `dist/intention.exe`(Windows):可执行文件
- `build/`:临时构建文件(可删除)
- `intention.spec`:打包配置文件(可保留用于后续修改)
查看打包后的文件包含的内容:
使用 `--log-level DEBUG` 查看详细信息:
pyinstaller -F --log-level DEBUG intention.py关于打包时环境说明:
打包时不会把环境中所有包都打包进去,PyInstaller会分析脚本的导入语句,只打包实际使用的模块及其依赖。环境中存在但未使用的包不会被包含。
**建议**:使用虚拟环境(venv)进行打包,确保只安装必要的依赖:
python3 -m venv venv source venv/bin/activate # Linux # 或 venv\Scripts\activate # Windows pip install -r requirements.txt pyinstaller pyinstaller -F -n intention --add-data "config.yaml:." intention.py5.4 运行可执行文件
**Linux环境**:
chmod +x dist/intention ./dist/intention**Windows环境**:
dist\intention.exe六、跨平台打包注意事项
6.1 重要原则
**⚠️关键点**:在Windows上打包的可执行文件只能在Windows上运行,在Linux上打包的可执行文件只能在Linux上运行。
**解决方案**:
1. **推荐**:在目标运行环境(Linux)上直接打包
2. **备选**:使用Docker容器或WSL在Linux环境中打包
3. **不推荐**:在Windows上打包后部署到Linux(不可行)
6.2 打包环境要求
- **打包机器**:需要安装Python和所有依赖包
- **运行机器**:无需安装Python,直接运行可执行文件即可
6.3 配置文件处理
打包时使用 `--add-data` 参数可以将配置文件一起打包,但需要注意:
1. **配置文件位置**:打包后,配置文件会被解压到临时目录
2. **读取方式**:需要使用PyInstaller的特殊路径处理方式读取配置文件
**改进的配置读取代码**(支持打包后的可执行文件):
import os import sys import yaml def resource_path(relative_path): """获取资源文件的绝对路径,支持打包后的可执行文件""" try: # PyInstaller创建的临时文件夹路径 base_path = sys._MEIPASS except Exception: # 开发环境中的路径 base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) def load_config(): try: # 优先尝试从可执行文件同目录读取(便于修改) config_path = os.path.join(os.path.dirname(sys.executable), "config.yaml") if not os.path.exists(config_path): # 如果不存在,尝试从打包的资源中读取 config_path = resource_path("config.yaml") with open(config_path, "r", encoding="utf-8") as f: return yaml.safe_load(f) or {} except FileNotFoundError: print("⚠️ 未找到 config.yaml,使用默认配置") return {}这样修改后,可以:
- 在可执行文件同目录放置 `config.yaml` 进行配置(推荐)
- 如果不存在,则使用打包时内置的默认配置
七、完整打包脚本示例
为了方便重复打包,可以创建一个打包脚本 `build.sh`(Linux):
#!/bin/bash echo "开始打包..." # 清理之前的构建文件 rm -rf build dist *.spec # 打包 pyinstaller -F \ -n intention \ --add-data "config.yaml:." \ --clean \ intention.py # 复制配置文件到dist目录(便于修改) cp config.yaml dist/ echo "打包完成!可执行文件位于: dist/intention"Windows版本 `build.bat`:
@echo off echo 开始打包... rmdir /s /q build dist *.spec 2>nul pyinstaller -F -n intention --add-data "config.yaml;." --clean intention.py copy config.yaml dist\ echo 打包完成!可执行文件位于: dist\intention.exe**项目结构**:
```
项目目录/
├── intention.py # 主脚本
├── config.yaml # 配置文件
├── requirements.txt # 依赖列表
├── build.sh # 打包脚本(Linux)
└── dist/
└── intention # 生成的可执行文件
```