公众号文章转播客 (WeChat Article → Podcast)
Overview
把一条 mp.weixin.qq.com 链接,变成一段 3 分钟以内、能直接下载收听的语音。
整条流水线三步走,每一步都有明确产物:
① 链接 ──抓取──▶ 全文 article.txt
② 全文 ──AI 提炼──▶ 口播稿 script.md(700–800 字,≈2分45秒)
③ 口播稿 ──合成──▶ podcast.mp3 + podcast.srt ← 交付给用户
核心原则:这不是「机械朗读全文」,而是先做减法再发声。一篇 5000 字的长文,压成 750 字的口播稿,保留最有信息量的骨架,用耳朵听得进去的语言重写。
环境准备
脚本依赖统一装在托管 venv 中,所有命令都用这个绝对路径的解释器:
PY=/Users/mac/.workbuddy/binaries/python/envs/default/bin/python
依赖:edge-tts、requests、beautifulsoup4、lxml(均已安装)。
若报 ModuleNotFoundError,执行:
/Users/mac/.workbuddy/binaries/python/envs/default/bin/pip install edge-tts requests beautifulsoup4 lxml
工作目录约定:所有产物放在当前工作区的 output/podcast/<文章短名>/ 下。
Step 1 · 拿到链接并抓取正文
1.1 索要链接
如果用户还没给链接,直接问,一句话就够:
> 把公众号文章链接发我就行(https://mp.weixin.qq.com/s/... 这种)。
同时可以顺带确认两个可选项(用户不说就用默认):
- 音色:默认晓晓(女声,温柔自然)
- 时长:默认 3 分钟以内
1.2 执行抓取
$PY ~/.workbuddy/skills/wechat-article-to-podcast/scripts/fetch_article.py \
"<文章链接>" \
--outdir ./output/podcast/<短名> \
--name article
输出:
- article.txt — 带元信息头的纯文本正文
- article.json — 结构化数据(title / account / author / publish_time / word_count / content)
- stdout 一段 JSON 摘要,先看 ok 和 word_count
1.3 抓取失败的降级路径(按顺序尝试)
脚本退出码 2 表示「页面拿到了但正文为空」,通常是反爬校验页。不要放弃,依次降级:
- 换 WebFetch 工具直接读该 URL,让它返回正文文本,手动存成
article.txt - 加载
agent-browser技能,用真实浏览器打开页面后提取正文(对付「环境异常」最有效) - 前两条都不行,才告诉用户:请把文章正文直接粘贴给我,后续步骤完全一样
退出码 3 是网络失败,直接重试或让用户确认链接是否可访问。
1.4 抓完先汇报
拿到正文后,用一两句话向用户确认抓对了:
> 抓到了:《文章标题》(公众号:XXX,全文 4200 字)。开始提炼口播稿。
Step 2 · 提炼 3 分钟口播稿
这一步由你(AI)亲自写,不要调脚本。 这是整个技能里质量差距最大的环节。
2.1 先读规范
动笔前必须读 references/podcast_script.md,它规定了:
- 字数与时长的换算基准(实测 285 字符 / 分钟,3 分钟 → 700–820 字符)
- 三幕式结构(钩子 / 主体 / 收尾)
- 书面语转口语的逐条改写规则
- TTS 友好处理(哪些符号、句式会翻车)
- 成稿自检清单
2.2 写稿要点速记
- 目标 700–820 字符(去空白、含标点计数),黄金值 780
- 开头 10 秒必须有钩子,禁止「大家好,今天我们来聊聊」
- 核心观点最多 3 个,每个都有口头序号做听觉锚点
- 保留原文的具体数字、案例名、金句——抽象结论听完就忘
- 单句 ≤ 30 字,句号是 TTS 唯一可靠的换气信号
- 无 Markdown 符号、无 emoji、无裸链接、无括号注释
2.3 存稿
写成 ./output/podcast/<短名>/script.md,并在对话里把全文展示给用户(这是可审阅的中间产物,用户可能要改)。
同时报一句字数与预估时长:
> 口播稿写好了,781 字,预计 2 分 44 秒。
如果用户对稿子提出修改意见,改完再进 Step 3。 不要在稿子没定的情况下急着合成。
Step 3 · 合成语音并交付
3.1 执行合成
$PY ~/.workbuddy/skills/wechat-article-to-podcast/scripts/tts.py \
--in ./output/podcast/<短名>/script.md \
--out ./output/podcast/<短名>/podcast.mp3 \
--voice zh-CN-XiaoxiaoNeural \
--srt --auto-fit --max-seconds 180
参数说明:
| 参数 | 作用 |
|---|---|
--voice |
音色,见下表;不确定就用默认晓晓 |
--rate |
语速,如 +10%;默认 +0% |
--srt |
顺带输出 .srt 字幕文件 |
--auto-fit |
超过 3 分钟自动提速重生成(最多 3 轮),保证卡住红线 |
--max-seconds |
时长上限,默认 180 秒 |
--engine |
auto(默认) / edge / say。auto 会在 edge-tts 不可用时自动切离线引擎 |
--list-voices |
列出所有可用中文音色 |
脚本返回 JSON,包含 audio 路径、duration_human、within_limit、size_kb。
务必检查 within_limit 为 true;若为 false 且没开 --auto-fit,回 Step 2 删字后重来。
3.2 常用音色
| 场景 | 音色 |
|---|---|
| 通用(默认) | zh-CN-XiaoxiaoNeural 晓晓 · 女 · 温柔自然 |
| 深度解读 / 商业分析 | zh-CN-YunjianNeural 云健 · 男 · 浑厚沉稳 |
| 资讯速报 | zh-CN-YunyangNeural 云扬 · 男 · 专业播报腔 |
| 轻松生活 | zh-CN-XiaoyiNeural 晓伊 · 女 · 活泼年轻 |
| 科技年轻向 | zh-CN-YunxiNeural 云希 · 男 · 阳光少年感 |
| 趣味反差 | zh-CN-liaoning-XiaobeiNeural 晓北 · 女 · 东北口音 |
3.3 交付(不可省略)
必须调用 present_files,把音频文件排在第一位:
present_files([
"<绝对路径>/podcast.mp3",
"<绝对路径>/script.md",
"<绝对路径>/podcast.srt"
])
然后用简短文字收尾,包含四个信息:时长、字数、音色、一句话内容概括。不要长篇复述稿子内容——用户点开就能听。
> ⚠️ 如果音频文件不在当前工作区内,需提醒用户在 WorkBuddy 小程序连接设置里打开「产物回传到小程序」开关,否则文件无法送达。
常见问题速查
| 症状 | 原因与处理 |
|---|---|
word_count: 0 + 提示「环境异常」 |
微信反爬。按 1.3 降级:WebFetch → agent-browser → 让用户粘贴正文 |
edge-tts 返回空音频 |
网络不通或音色名拼错。加 --engine say 走离线兜底 |
| 音频超过 3 分钟 | 加 --auto-fit;仍超就回 Step 2 把稿子删到 750 字以内 |
字幕 srt: null |
服务端未返回边界事件,非致命,音频照常可用 |
say 引擎输出 .aiff 而非 .m4a |
afconvert 转码失败时的兜底行为,文件可正常播放 |
| 合成出来读音怪异 | 稿子里有括号注释 / 英文缩写 / 符号。回 Step 2 按规范第四节改写 |
文件清单
wechat-article-to-podcast/
├── SKILL.md # 本文件,三步工作流总纲
├── scripts/
│ ├── fetch_article.py # Step 1 公众号正文提取
│ └── tts.py # Step 3 双引擎语音合成
└── references/
└── podcast_script.md # Step 2 口播稿写作规范(写稿前必读)