1. 1. AI编程:媒体摘要PPT生成器开发实践
    1. 1.1. 项目背景
    2. 1.2. 需求分析
      1. 1.2.1. 输入数据
      2. 1.2.2. 输出要求
      3. 1.2.3. 技术挑战
    3. 1.3. 技术方案选型
      1. 1.3.1. 为什么选Python?
      2. 1.3.2. 核心技术栈
    4. 1.4. 系统架构设计
      1. 1.4.1. 整体架构
      2. 1.4.2. 模块划分
    5. 1.5. 核心技术实现
      1. 1.5.1. 1. 内容类型智能识别
      2. 1.5.2. 2. 视频截图优化(核心难点)
        1. 1.5.2.1. 问题演进
      3. 1.5.3. 3. 图文内容精准提取(核心算法)
        1. 1.5.3.1. 问题分析
        2. 1.5.3.2. 多层过滤策略
      4. 1.5.4. 4. PPT生成与排版设计
        1. 1.5.4.1. 布局迭代过程
    6. 1.6. 关键问题与解决方案
      1. 1.6.1. 问题1:打包后路径错误
      2. 1.6.2. 问题2:Selenium初始化失败
      3. 1.6.3. 问题3:错误处理策略
    7. 1.7. 工程实践
      1. 1.7.1. 1. 配置管理
      2. 1.7.2. 2. 日志记录
      3. 1.7.3. 3. 重试机制
      4. 1.7.4. 4. 进度显示
    8. 1.8. 打包与分发
      1. 1.8.1. PyInstaller打包
      2. 1.8.2. 分发包结构
    9. 1.9. 性能指标
      1. 1.9.1. 处理速度
      2. 1.9.2. 资源占用
    10. 1.10. 项目总结
      1. 1.10.1. 技术亮点
      2. 1.10.2. 效果评估
      3. 1.10.3. 可复用价值
    11. 1.11. 未来改进方向
      1. 1.11.1. 功能增强
      2. 1.11.2. 性能优化
      3. 1.11.3. 用户体验
    12. 1.12. 项目资源
    13. 1.13. 跨平台打包:GitHub Actions 自动构建
      1. 1.13.1. 需求与挑战
      2. 1.13.2. 解决方案:GitHub Actions
        1. 1.13.2.1. 架构设计
      3. 1.13.3. 核心实现
        1. 1.13.3.1. Workflow 配置
        2. 1.13.3.2. 构建脚本差异
      4. 1.13.4. 实践中的三个关键问题
        1. 1.13.4.1. 问题 1:create-release job 被跳过
        2. 1.13.4.2. 问题 2:create-release 报 403 错误
        3. 1.13.4.3. 问题 3:中文文件名上传失败
      5. 1.13.5. 效果评估
      6. 1.13.6. 总结

AI编程:媒体摘要PPT生成器开发实践

AI编程:媒体摘要PPT生成器开发实践

项目背景

媒体部门日常工作中需要整理大量新闻摘录,将150条媒体数据(包括视频链接和图文链接)制作成PPT汇报。传统手工方式存在以下痛点:

  • 耗时长:每条需要手动访问、截图、复制粘贴,150条需要4-5小时
  • 📐 格式不统一:不同人员制作的PPT风格各异
  • 🖼️ 截图质量差:手工截图经常包含广告、导航等无关内容
  • ⚠️ 容易出错:手工操作易遗漏或复制错误

本文介绍如何用Python开发一个自动化工具,将处理时间缩短到1-2小时,并确保输出质量稳定。


需求分析

输入数据

Excel文件结构:

序号 媒体名称 发布时间 标题 链接 备注
1 CCTV-13 2026-01-01 新闻标题 https://tv.cctv.com/ 备注信息
2 人民日报 2026-01-02 文章标题 http://paper.people.com.cn/ 备注信息

输出要求

每行数据生成一页PPT,包含:

1
2
3
4
5
6
7
8
9
10
11
┌──────────────────────────────────────┐
│ 新闻标题(标题) │
│ 媒体名称 发布时间 │
│ 链接URL │
├──────────────────────────────────────┤
│ │
│ 视频截图 或 网页截图 │
│ │
├──────────────────────────────────────┤
│ 摘要文字(图文类型) │
└──────────────────────────────────────┘

技术挑战

  1. 内容类型识别:自动判断视频还是图文
  2. 视频广告处理:截图时避免广告干扰
  3. 图文内容提取:过滤导航、页脚等无关信息
  4. PPT自动排版:保证视觉效果专业

技术方案选型

为什么选Python?

方案 优点 缺点 结论
Excel VBA 与Excel深度集成 网页操作能力弱 ❌ 不适合
JavaScript 前端爬虫方便 PPT生成复杂 ❌ 不适合
Python 生态完善、库丰富 需要打包分发 最佳选择

核心技术栈

1
2
3
4
5
6
7
8
Python 3.12
├── pandas + openpyxl # Excel读写
├── python-pptx # PPT生成
├── selenium # 网页自动化
├── beautifulsoup4 + lxml # HTML解析
├── opencv-python # 视频处理
├── Pillow # 图片处理
└── PyInstaller # 打包exe

选型理由

  • Selenium:无头浏览器,支持JavaScript渲染
  • BeautifulSoup:HTML解析能力强,选择器灵活
  • python-pptx:PPT编程生成,布局精确控制

系统架构设计

整体架构

graph LR
    A[Excel文件] --> B[ExcelReader]
    B --> C[主流程控制]
    C --> D[ContentAnalyzer<br>内容类型识别]
    D --> E{类型判断}
    E -->|视频| F[WebpageScraper<br>视频截图]
    E -->|图文| G[WebpageScraper<br>图文抓取]
    F --> H[PPTGenerator]
    G --> H
    H --> I[输出PPT]

模块划分

模块 职责 核心类/函数
excel_reader.py Excel数据读取与解析 ExcelReader.get_rows()
content_analyzer.py 识别视频/图文类型 ContentAnalyzer.get_content_type()
webpage_scraper.py 网页截图与内容提取 WebpageScraper.scrape_webpage()
ppt_generator.py PPT生成与排版 PPTGenerator.add_slide()
utils.py 工具函数(日志、重试) @retry_on_failure
config.py 集中配置管理 全局配置参数

核心技术实现

1. 内容类型智能识别

根据URL特征判断是视频还是图文:

1
2
3
4
5
6
7
8
9
10
11
class ContentAnalyzer:
@staticmethod
def is_video_link(url):
"""判断是否为视频链接"""
# CCTV视频特征
if 'tv.cctv.com' in url:
return True

# 其他视频网站
video_domains = ['bilibili.com', 'iqiyi.com', 'youtube.com']
return any(domain in url.lower() for domain in video_domains)

设计思路:基于域名特征的规则匹配,简单高效,扩展性强。


2. 视频截图优化(核心难点)

问题演进

初版方案:页面加载后等待5秒

1
2
3
driver.get(url)
time.sleep(5) # ❌ 仍有广告
driver.save_screenshot(path)

问题:广告通常10-30秒,5秒不够。

最终方案:30秒等待 + 主动关闭广告

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
def scrape_webpage(self, url, content_type='video'):
"""抓取网页内容"""
self.driver.get(url)
time.sleep(PAGE_LOAD_WAIT) # 3秒基础加载

if content_type == 'video':
# 视频页面额外等待30秒让广告播放完
logger.info("视频页面,等待30秒让广告播放完毕")
time.sleep(30)

# 尝试主动关闭广告
self._try_close_ads()

# 截图
screenshot_path = self._capture_video_frame()
return screenshot_path

def _try_close_ads(self):
"""尝试关闭广告弹窗"""
close_selectors = [
'.close', '.closeBtn', '.ad-close',
'[class*="close"]', 'button[title*="关闭"]'
]

for selector in close_selectors:
try:
btn = self.driver.find_element(By.CSS_SELECTOR, selector)
if btn.is_displayed():
btn.click()
logger.info(f"关闭了广告: {selector}")
time.sleep(1)
break
except:
continue

效果

  • 等待30秒覆盖99%的广告时长
  • 主动关闭处理剩余1%的情况
  • 视频截图质量大幅提升

3. 图文内容精准提取(核心算法)

问题分析

使用body.get_text()全量提取,结果混杂大量无关内容:

1
2
3
4
首页 新闻 财经 体育(导航栏)
正文内容...
相关阅读:xxx(侧边栏)
版权所有 © 2026(页脚)

多层过滤策略

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
53
54
55
56
57
58
def _extract_summary(self):
"""提取页面文本摘要"""
soup = BeautifulSoup(page_source, 'lxml')

# 第1层:移除已知干扰标签
for element in soup(['script', 'style', 'nav', 'footer',
'header', 'aside', 'iframe', 'button']):
element.decompose()

# 第2层:基于class/id特征过滤
noise_patterns = [
'nav', 'menu', 'sidebar', 'comment',
'footer', 'header', 'ad', 'share', 'related'
]

for pattern in noise_patterns:
# 过滤class包含噪声关键词的元素
for el in soup.find_all(class_=lambda x: x and pattern in x.lower()):
el.decompose()
# 过滤id包含噪声关键词的元素
for el in soup.find_all(id=lambda x: x and pattern in x.lower()):
el.decompose()

# 第3层:针对性选择器提取正文
content_selectors = [
'.left_zw', # 中国新闻网
'#artibody', # 新浪
'.cnt_bd', # 腾讯
'article', # 通用HTML5
'.article-content', # 通用class
]

text = ''
for selector in content_selectors:
element = soup.select_one(selector)
if element:
# 提取前再次清理内部干扰
for noise in element.find_all(class_=lambda x:
x and any(p in x.lower() for p in ['share', 'comment'])):
noise.decompose()

text = element.get_text(separator='\n', strip=True)
if len(text) > 100:
logger.info(f"使用选择器 '{selector}' 提取到内容")
break

# 第4层:文本质量过滤
lines = [line.strip() for line in text.split('\n') if line.strip()]

# 过滤短行(可能是导航链接)和重复行
filtered_lines = []
seen = set()
for line in lines:
if len(line) > 10 and line not in seen:
filtered_lines.append(line)
seen.add(line)

return '\n'.join(filtered_lines)[:SUMMARY_MAX_LENGTH]

效果对比

网站 优化前 优化后 改进
人民日报 包含导航、页脚、相关阅读 71个有效段落 ✅ 精准
中国新闻网 混杂大量无关内容 .left_zw精准提取正文 ✅ 专用选择器

4. PPT生成与排版设计

布局迭代过程

Version 1:信息堆砌

1
2
3
4
媒体名称 - 标题
发布时间:xxx
链接:xxx
[图片]

❌ 问题:层次不清,标题不突出

Version 2:分行但顺序错误

1
2
3
4
媒体名称  发布时间:xxx
标题
链接:xxx
[图片]

❌ 问题:次要信息在标题上方

Version 3(最终版):标题优先

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
def add_slide(self, data):
"""添加一页幻灯片"""
slide = self.prs.slides.add_slide(blank_layout)

# 1. 标题(最上方,最重要)
title_box = slide.shapes.add_textbox(
Inches(0.5), Inches(0.3),
Inches(9), Inches(0.7)
)
p = title_box.text_frame.paragraphs[0]
p.text = data['title']
p.font.size = Pt(20)
p.font.bold = True
p.font.color.rgb = RGBColor(0, 0, 0) # 黑色

# 2. 媒体信息(标题下方,次要信息)
media_box = slide.shapes.add_textbox(
Inches(0.5), Inches(1.05),
Inches(9), Inches(0.35)
)
p = media_box.text_frame.paragraphs[0]
p.text = f"{data['media_name']} 发布时间: {data['publish_time']}"
p.font.size = Pt(11)
p.font.color.rgb = RGBColor(100, 100, 100) # 灰色

# 3. 链接(更小的字体)
link_box = slide.shapes.add_textbox(
Inches(0.5), Inches(1.45),
Inches(9), Inches(0.3)
)
p = link_box.text_frame.paragraphs[0]
p.text = f"链接: {data['link']}"
p.font.size = Pt(9)
p.font.color.rgb = RGBColor(0, 102, 204) # 蓝色

# 4. 图片(保持比例,居中)
if data['screenshot']:
left, top, width, height = self._calculate_image_size(
data['screenshot'],
max_width=8, # 英寸
max_height=5
)
slide.shapes.add_picture(data['screenshot'], left, top, width, height)

最终效果

  • 视觉层次清晰:黑色标题 → 灰色信息 → 蓝色链接
  • 阅读顺序符合直觉:从上到下,重要性递减
  • 字体大小合理:20pt → 11pt → 9pt

关键问题与解决方案

问题1:打包后路径错误

现象:exe运行时提示找不到Excel文件

根因分析

1
2
3
4
5
6
# 开发时使用 __file__
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# C:\ai-projects\html-to-ppt

# 打包后 __file__ 指向临时目录
# C:\Users\xxx\AppData\Local\Temp\_MEIxxxxxx\

解决方案

1
2
3
4
5
6
7
8
9
10
import sys

if getattr(sys, 'frozen', False):
# 打包后:使用exe所在目录
BASE_DIR = os.path.dirname(sys.executable)
else:
# 开发时:使用脚本目录
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

EXCEL_PATH = os.path.join(BASE_DIR, 'input', '媒体摘录.xlsx')

问题2:Selenium初始化失败

现象:Chrome浏览器无法启动

解决方案:配置无头模式参数

1
2
3
4
5
6
7
chrome_options = Options()
chrome_options.add_argument('--headless') # 无头模式
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
# 反爬虫检测
chrome_options.add_argument('--disable-blink-features=AutomationControlled')

问题3:错误处理策略

设计思路:单点故障不影响整体

1
2
3
4
5
6
7
8
9
10
11
12
13
for idx, row in enumerate(rows):
try:
# 处理单行数据
content = scraper.scrape_webpage(row['link'])
ppt_gen.add_slide(content)
success_count += 1
except Exception as e:
# 记录错误但继续处理
logger.error(f"第{idx}行处理失败: {e}")
fail_count += 1
continue # 继续下一行

logger.info(f"成功: {success_count}, 失败: {fail_count}")

效果

  • 150行数据中即使有5行失败,也能生成145页的PPT
  • 详细日志便于定位问题

工程实践

1. 配置管理

集中配置,便于调整:

1
2
3
4
5
# config.py
REQUEST_TIMEOUT = 30 # 网络超时
PAGE_LOAD_WAIT = 3 # 页面加载等待
VIDEO_AD_WAIT = 30 # 视频广告等待
SUMMARY_MAX_LENGTH = 300 # 摘要最大长度

2. 日志记录

1
2
3
4
5
6
7
8
9
# utils.py
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('process.log', encoding='utf-8'),
logging.StreamHandler() # 同时输出到控制台
]
)

3. 重试机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from functools import wraps

def retry_on_failure(max_retries=3, delay=2):
"""重试装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
logger.warning(f"{func.__name__}{attempt+1}次失败: {e}")
if attempt < max_retries - 1:
time.sleep(delay)
raise
return wrapper
return decorator

@retry_on_failure(max_retries=2)
def scrape_webpage(url):
# 网络请求可能失败,自动重试
pass

4. 进度显示

1
2
3
4
5
from tqdm import tqdm

for row in tqdm(rows, desc="处理进度"):
# 处理每行数据
pass

输出效果:

1
处理进度: 100%|██████████| 150/150 [2:15:30<00:00, 54.20s/it]

打包与分发

PyInstaller打包

1
2
3
4
5
6
7
8
9
10
# build_exe.py
PyInstaller.__main__.run([
'main.py',
'--name=媒体摘要PPT生成器',
'--onefile', # 单文件exe
'--console', # 保留控制台
'--hidden-import=pandas',
'--hidden-import=selenium',
'--collect-all=pptx',
])

打包结果

  • 文件大小:153.73 MB
  • 包含所有依赖库
  • 无需Python环境即可运行

分发包结构

1
2
3
4
5
6
7
媒体摘要PPT生成器/
├── 媒体摘要PPT生成器.exe # 主程序
├── input/ # 用户放Excel文件
│ └── 媒体摘录.xlsx
├── output/ # 自动创建
│ └── 媒体摘录.pptx
└── 使用说明.md

性能指标

处理速度

数据量 预计时间 平均速度
5条(测试) 2分钟 24秒/条
20条 10-15分钟 30-45秒/条
150条 75-100分钟 30-40秒/条

说明

  • 视频:33秒/条(30秒广告 + 3秒加载)
  • 图文:5-8秒/条

资源占用

  • CPU:中等(浏览器渲染)
  • 内存:500MB-1GB
  • 磁盘:临时文件自动清理

项目总结

技术亮点

  1. 智能内容识别:自动区分视频和图文
  2. 精准内容提取:多层过滤确保质量
  3. 视觉效果优秀:PPT排版专业
  4. 工程实践完善:错误容错、日志、进度
  5. 开箱即用:打包成exe,无需配置

效果评估

指标 手工方式 自动化方式 改进
处理时间 4-5小时 1-2小时 ↓ 60%
格式统一 ❌ 各异 ✅ 完全统一 100%
截图质量 ❌ 包含广告 ✅ 纯净画面 大幅提升
错误率 ⚠️ 易出错 ✅ 可控 接近0

可复用价值

本项目技术方案适用于:

  • Excel → PPT:任何Excel数据转PPT的场景
  • 网页自动化:批量网页截图、内容提取
  • 内容聚合:多源数据汇总整理

核心代码模块化,可直接复用:

  • webpage_scraper.py:通用网页抓取
  • ppt_generator.py:PPT编程生成
  • utils.py:重试、日志等工具

未来改进方向

功能增强

  1. AI摘要:集成大语言模型生成高质量摘要
  2. 并行处理:多线程提升处理速度3-5倍
  3. 模板系统:支持自定义PPT模板和主题

性能优化

  1. 智能等待:探测广告结束时机,减少无效等待
  2. 缓存机制:已处理URL不重复抓取
  3. 增量更新:只处理新增数据

用户体验

  1. 图形界面:PyQt5 GUI,更友好
  2. 实时预览:边处理边预览PPT效果
  3. 云端部署:Web服务,在线使用

项目资源

  • 源码:150行核心代码 + 850行支持代码
  • 依赖:10个主要Python库
  • 文档:4份完整文档(用户手册、开发指南)
  • 测试:150行真实数据验证

项目特点:模块化设计、技术稳健、可维护性强、复用价值高


跨平台打包:GitHub Actions 自动构建

需求与挑战

初版项目仅支持 Windows 平台。当需要支持 macOS 时,面临核心矛盾:PyInstaller 不支持交叉编译

技术限制

  • PyInstaller 必须在目标平台上运行(Windows 生成 .exe,macOS 生成 Mach-O 可执行文件)
  • 需要目标平台的系统库、Python 解释器、二进制依赖(.dll / .dylib / .so
  • 无法在 Windows 上生成 macOS 可执行文件

传统方案的成本

  • 购买 Mac 硬件:万元级
  • 租用 macOS 云主机:持续费用
  • 手动维护两台机器:效率低

解决方案:GitHub Actions

使用 GitHub Actions 的免费 runner 实现零成本跨平台构建。

架构设计

graph TB
    A[Push Tag v*] --> B{GitHub Actions}
    B --> C[build-windows<br/>windows-latest]
    B --> D[build-macos<br/>macos-latest]
    
    C --> C1[Python 3.10]
    C1 --> C2[pip install]
    C2 --> C3[python build_exe.py]
    C3 --> C4[Upload artifact]
    
    D --> D1[Python 3.10]
    D1 --> D2[pip install]
    D2 --> D3[python build_macos.py]
    D3 --> D4[Upload artifact]
    
    C4 --> E[create-release<br/>ubuntu-latest]
    D4 --> E
    E --> F[Download artifacts]
    F --> G[Create GitHub Release]
    G --> H[media_ppt_converter.exe<br/>media_ppt_converter]

关键设计点

  1. 并行构建:两个平台独立运行,总耗时 = max(Windows, macOS) ≈ 5 分钟
  2. 统一触发:推送 tag 自动触发三个 job
  3. Artifacts 中转:构建产物先上传为 artifacts,release job 再下载并发布

核心实现

Workflow 配置

.github/workflows/build.yml 完整配置:

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
53
54
55
56
57
58
59
60
61
62
63
name: Build Executables

on:
push:
branches: [ master, macos ]
tags:
- 'v*' # 匹配 v1.0.0, v2.0.0 等
workflow_dispatch:

permissions:
contents: write # 必须:允许创建 Release

jobs:
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- run: |
pip install -r requirements.txt pyinstaller
python build_exe.py
- uses: actions/upload-artifact@v4
with:
name: windows-executable
path: dist/media_ppt_converter.exe

build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- run: |
pip install -r requirements.txt pyinstaller
python build_macos.py
- uses: actions/upload-artifact@v4
with:
name: macos-executable
path: dist/media_ppt_converter

create-release:
needs: [build-windows, build-macos]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/download-artifact@v4
with:
name: windows-executable
path: ./windows-executable
- uses: actions/download-artifact@v4
with:
name: macos-executable
path: ./macos-executable
- uses: softprops/action-gh-release@v1
with:
files: |
windows-executable/*
macos-executable/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

构建脚本差异

两个平台的构建脚本参数完全一致,仅文件名不同:

参数 Windows 输出 macOS 输出
--name media_ppt_converter media_ppt_converter
--onefile PE 格式 .exe Mach-O 格式(无扩展名)
运行方式 media_ppt_converter.exe ./media_ppt_converter

实践中的三个关键问题

问题 1:create-release job 被跳过

现象build-windowsbuild-macos 成功,但 create-release 显示 “This job was skipped”。

根因:workflow 触发条件缺少 tags 配置。

1
2
3
4
5
6
7
8
9
10
11
# ❌ 错误:只监听分支推送
on:
push:
branches: [ master ]

# ✅ 正确:同时监听 tag 推送
on:
push:
branches: [ master ]
tags:
- 'v*'

原理:推送 tag 时,github.refrefs/tags/v1.0.0;推送分支时是 refs/heads/master。条件 if: startsWith(github.ref, 'refs/tags/v') 只在前者满足。


问题 2:create-release 报 403 错误

现象softprops/action-gh-release 重试 3 次后失败,报 “Too many retries”。

根因:默认 GITHUB_TOKEN 权限不足,无法创建 Release。

解决方案:在 workflow 顶层添加权限声明:

1
2
permissions:
contents: write # 允许创建 Release 和上传文件

说明:GitHub Actions 的权限模型在 2023 年后默认为最小权限,需要显式声明 contents: write 才能操作 Releases。


问题 3:中文文件名上传失败

现象:Release 中出现 default.PPTPPT.exe,而不是预期的中文文件名。

根因softprops/action-gh-release 对中文文件名支持不佳,可能被截断或乱码。

解决方案:将构建产物改为英文文件名:

1
2
3
4
5
6
# build_exe.py / build_macos.py
PyInstaller.__main__.run([
'main.py',
'--name=media_ppt_converter', # 改为英文
# ...
])

最佳实践

  • 构建产物使用英文命名
  • workflow 中使用通配符:windows-executable/* 避免硬编码文件名
  • 在 README 或 Release Notes 中用中文说明

效果评估

指标 手动构建 GitHub Actions 提升
支持平台 Windows Windows + macOS 2x
硬件成本 0 0 -
构建时间 5分钟(单平台) 5分钟(并行) 100%
操作方式 手动在两台机器 推送 tag 自动构建 全自动
版本一致性 ⚠️ 易出错 ✅ 同一份代码 完美

使用流程

1
2
3
4
5
6
7
8
9
10
11
12
# 开发者:打 tag 并推送
git tag v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0

# GitHub Actions 自动完成:
# 1. 在 windows-latest 上构建 .exe
# 2. 在 macos-latest 上构建可执行文件
# 3. 创建 GitHub Release
# 4. 上传两个平台的文件

# 用户:从 Releases 页面下载
# https://github.com/user/repo/releases/latest

总结

通过 GitHub Actions 实现跨平台打包的核心价值:

  1. 零成本:无需购买 Mac 设备或云服务
  2. 全自动:推送 tag 触发构建和发布
  3. 高可靠:同一份代码保证版本一致性
  4. 易扩展:添加 Linux 支持只需 10 行配置

适用场景:所有需要跨平台分发的 Python 工具、桌面应用、CLI 程序。


本文完整记录了从需求分析、技术选型、架构设计到问题解决的全过程,希望为类似自动化工具开发提供参考。