AI编程:媒体摘要PPT生成器开发实践 项目背景 媒体部门日常工作中需要整理大量新闻摘录,将150条媒体数据(包括视频链接和图文链接)制作成PPT汇报。传统手工方式存在以下痛点:
⏰ 耗时长 :每条需要手动访问、截图、复制粘贴,150条需要4-5小时
📐 格式不统一 :不同人员制作的PPT风格各异
🖼️ 截图质量差 :手工截图经常包含广告、导航等无关内容
⚠️ 容易出错 :手工操作易遗漏或复制错误
本文介绍如何用Python开发一个自动化工具,将处理时间缩短到1-2小时,并确保输出质量稳定。
需求分析 输入数据 Excel文件结构:
输出要求 每行数据生成一页PPT,包含:
1 2 3 4 5 6 7 8 9 10 11 ┌──────────────────────────────────────┐ │ 新闻标题(标题) │ │ 媒体名称 发布时间 │ │ 链接URL │ ├──────────────────────────────────────┤ │ │ │ 视频截图 或 网页截图 │ │ │ ├──────────────────────────────────────┤ │ 摘要文字(图文类型) │ └──────────────────────────────────────┘
技术挑战
内容类型识别 :自动判断视频还是图文
视频广告处理 :截图时避免广告干扰
图文内容提取 :过滤导航、页脚等无关信息
PPT自动排版 :保证视觉效果专业
技术方案选型 为什么选Python?
方案
优点
缺点
结论
Excel VBA
与Excel深度集成
网页操作能力弱
❌ 不适合
JavaScript
前端爬虫方便
PPT生成复杂
❌ 不适合
Python
生态完善、库丰富
需要打包分发
✅ 最佳选择
核心技术栈 1 2 3 4 5 6 7 8 Python 3.12 ├── pandas + openpyxl ├── python-pptx ├── selenium ├── beautifulsoup4 + lxml ├── opencv-python ├── Pillow └── PyInstaller
选型理由 :
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 ): """判断是否为视频链接""" 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) if content_type == 'video' : 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' ) for element in soup(['script' , 'style' , 'nav' , 'footer' , 'header' , 'aside' , 'iframe' , 'button' ]): element.decompose() noise_patterns = [ 'nav' , 'menu' , 'sidebar' , 'comment' , 'footer' , 'header' , 'ad' , 'share' , 'related' ] for pattern in noise_patterns: for el in soup.find_all(class_=lambda x: x and pattern in x.lower()): el.decompose() for el in soup.find_all(id =lambda x: x and pattern in x.lower()): el.decompose() content_selectors = [ '.left_zw' , '#artibody' , '.cnt_bd' , 'article' , '.article-content' , ] 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 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) 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 ) 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 ) 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 ) 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 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
解决方案 :
1 2 3 4 5 6 7 8 9 10 import sysif getattr (sys, 'frozen' , False ): 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 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 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 wrapsdef 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 tqdmfor 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 PyInstaller.__main__.run([ 'main.py' , '--name=媒体摘要PPT生成器' , '--onefile' , '--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
磁盘:临时文件自动清理
项目总结 技术亮点
✨ 智能内容识别 :自动区分视频和图文
✨ 精准内容提取 :多层过滤确保质量
✨ 视觉效果优秀 :PPT排版专业
✨ 工程实践完善 :错误容错、日志、进度
✨ 开箱即用 :打包成exe,无需配置
效果评估
指标
手工方式
自动化方式
改进
处理时间
4-5小时
1-2小时
↓ 60%
格式统一
❌ 各异
✅ 完全统一
100%
截图质量
❌ 包含广告
✅ 纯净画面
大幅提升
错误率
⚠️ 易出错
✅ 可控
接近0
可复用价值 本项目技术方案适用于:
Excel → PPT :任何Excel数据转PPT的场景
网页自动化 :批量网页截图、内容提取
内容聚合 :多源数据汇总整理
核心代码模块化,可直接复用:
webpage_scraper.py:通用网页抓取
ppt_generator.py:PPT编程生成
utils.py:重试、日志等工具
未来改进方向 功能增强
AI摘要 :集成大语言模型生成高质量摘要
并行处理 :多线程提升处理速度3-5倍
模板系统 :支持自定义PPT模板和主题
性能优化
智能等待 :探测广告结束时机,减少无效等待
缓存机制 :已处理URL不重复抓取
增量更新 :只处理新增数据
用户体验
图形界面 :PyQt5 GUI,更友好
实时预览 :边处理边预览PPT效果
云端部署 :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]
关键设计点 :
并行构建 :两个平台独立运行,总耗时 = max(Windows, macOS) ≈ 5 分钟
统一触发 :推送 tag 自动触发三个 job
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*' workflow_dispatch: permissions: contents: write 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-windows 和 build-macos 成功,但 create-release 显示 “This job was skipped”。
根因 :workflow 触发条件缺少 tags 配置。
1 2 3 4 5 6 7 8 9 10 11 on: push: branches: [ master ] on: push: branches: [ master ] tags: - 'v*'
原理 :推送 tag 时,github.ref 是 refs/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
说明 :GitHub Actions 的权限模型在 2023 年后默认为最小权限,需要显式声明 contents: write 才能操作 Releases。
问题 3:中文文件名上传失败 现象 :Release 中出现 default.PPT 和 PPT.exe,而不是预期的中文文件名。
根因 :softprops/action-gh-release 对中文文件名支持不佳,可能被截断或乱码。
解决方案 :将构建产物改为英文文件名:
1 2 3 4 5 6 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 git tag v1.0.0 -m "Release v1.0.0" git push origin v1.0.0
总结 通过 GitHub Actions 实现跨平台打包的核心价值:
✅ 零成本 :无需购买 Mac 设备或云服务
✅ 全自动 :推送 tag 触发构建和发布
✅ 高可靠 :同一份代码保证版本一致性
✅ 易扩展 :添加 Linux 支持只需 10 行配置
适用场景 :所有需要跨平台分发的 Python 工具、桌面应用、CLI 程序。
本文完整记录了从需求分析、技术选型、架构设计到问题解决的全过程,希望为类似自动化工具开发提供参考。