微信公众号文章归档器
来源:freestylefly/wechat-article-archive-skill(作者苍何),适配为 Hermes Skill 格式。 原始许可:MIT
概述
从用户提供的公开微信公众号文章链接出发,自动识别博主身份,采集可公开访问的文章正文与图片,按公众号隔离保存为本地 Markdown 归档,生成文章清单,并按需调用 author-methodology-analysis 生成方法论报告与文案框架。进入分析流程后默认自动生成 HTML 看板并同步飞书,最后校验并打包 ZIP。
触发条件
通用领域触发矩阵
覆盖 5 大领域、25 个子场景。自动识别触发。
AI 领域
| 场景 | 触发信号 |
|---|---|
| AI产品竞品监控 | "采集XX AI公司的公众号文章""归档 OpenAI/Anthropic 公众号" |
| AI行业趋势追踪 | "抓一下AI行业KOL最近的文章""归档歸藏/宝玉的公众号" |
| AI博主方法论学习 | "采集XX的公众号,分析他的写作方法论" |
| AI训练语料收集 | "归档这些AI技术文章作为参考语料" |
咨询领域
| 场景 | 触发信号 |
|---|---|
| 客户行业快速研究 | "帮我采集XX行业的公众号文章做研究""快速建立XX行业的公众号内容档案" |
| 竞品情报交付 | "把竞品的公众号文章全部归档""采集XX公司的公众号历史文章" |
| 行业最佳实践采集 | "归档行业头部公司的公众号""采集行业协会的公众号内容" |
| 尽调辅助 | "做DD需要这个公司的公开公众号内容" |
企业管理领域
| 场景 | 触发信号 |
|---|---|
| 行业政策追踪 | "自动追踪XX部门的公众号""监控行业协会的公众号更新" |
| 知识管理基础设施 | "把内部公众号文章归档到知识库""建立公司公众号内容档案" |
| 品牌内容资产管理 | "归档我们自己的公众号历史文章""整理企业公众号内容库" |
| 竞品动态监控 | "持续监控竞品公众号""追踪XX公司的公众号更新" |
数字化领域
| 场景 | 触发信号 |
|---|---|
| 内容数字化转型 | "把公众号文章导出为Markdown""微信文章转本地归档" |
| 数据主权迁移 | "把微信平台的文章迁出来""公众号内容脱离微信保存" |
| 多平台内容聚合 | "把公众号文章同步到飞书知识库" |
通用内容采集
| 场景 | 触发信号 |
|---|---|
| 公众号文章归档 | "公众号文章归档""采集公众号最近N篇""公众号带图Markdown" |
| 博主内容采集 | "采集这个博主的所有文章""抓取XX公众号的内容" |
| 批量公众号处理 | "采集这几个公众号""批量归档公众号" |
手动触发关键词
采集公众号、公众号归档、公众号文章下载、微信文章转Markdown、公众号内容监控、归档公众号、抓取公众号、公众号图片本地化、公众号ZIP打包、wechat-article-archive、微信文章采集、公众号批量采集
不触发
- 需要登录/付费才能访问的私密内容
- 绕过验证码或反爬机制的请求
- 非微信公众号平台的内容
默认行为
- 只采集用户有权访问的公开内容,正文抓取不使用 Cookie
- 精确获取公众号历史列表时可由用户扫码登录微信公众平台,登录态仅保存在本地缓存
- 用户可自定义采集篇数;未指定时默认目标为最近 50 篇
- 默认生成文章归档、
<博主>-文章清单.csv和 ZIP - 仅当用户要求分析作者方法论时,调用
author-methodology-analysis - 进入方法论分析流程后,默认自动生成 HTML 看板并同步飞书
- 只有用户明确要求"不生成 HTML"或"不同步飞书"时才关闭对应产物
- 公开来源不足时交付实际数量,不伪造"最近 N 篇"或"完整历史"
唯一输出契约
先识别公众号名称和 biz,再确定:
author_root = <workspace>/output/<safe-author-name>/
若同名目录已属于其他 biz,使用 <safe-author-name>-<biz-tail>。
最终结构:
output/<safe-author-name>/
<safe-author-name>-文章清单.csv
articles/
01-文章标题/
<文章标题>.md
images/
<safe-author-name>-方法论报告.md # 仅按需分析
<safe-author-name>-文案框架.md # 仅按需分析
<safe-author-name>-分析数据.json # 分析时生成
<safe-author-name>-文章特征.csv # 分析时生成
<safe-author-name>-方法论看板.html # 分析时默认生成
<safe-author-name>-飞书同步.json # 成功同步飞书后生成
<safe-author-name>-文章归档.zip
工作流
1. 解析入口并锁定身份
🔴 CHECKPOINT → 🛑 STOP — 入口解析前确认:提供的文章链接是否为公开的 mp.weixin.qq.com URL?是否可公开访问(无需登录/付费)?
🛑 STOP 验证项:
- URL 域名为
mp.weixin.qq.com - 浏览器无痕模式可访问(无登录/付费墙)
- 已识别
biz、mid、idx、sn四元组
从入口文章提取:标题(#activity-name / msg_title)、公众号名(#js_name / nickname)、__biz / biz、mid / appmsgid、idx、sn、发布时间、合集信息。
2. 锁定候选列表
🔴 CHECKPOINT → 🛑 STOP — 候选列表生成前确认:是否需要扫码登录微信公众平台?扫码人是否就位(手机在身边)?采集篇数是否已指定(默认 50)?
🛑 STOP 验证项:
- 扫码人就位(手机在身边)或已确认走公开历史入口
- 采集篇数 N 已明确(默认 50)
- 输出目录
author_root已确定且无biz冲突
候选字段至少包括:title, url, publish_time, source_type, accessible, biz, mid, idx, sn
来源优先级:
- 用户扫码授权后的微信公众平台文章历史列表
- 无需登录即可分页的公众号公开历史入口
- 公开合集/专辑
- 当前文章页显式内链
- 最多两轮公开搜索补充
- 经身份核验的本地既有归档
候选按 biz + mid + idx + sn 和规范化 URL 去重。
用户要求"最近 N 篇"时,优先使用确定性历史列表脚本:
python3 scripts/discover_account_articles.py \
--account "<公众号精确名称>" \
--limit <N> \
--output "<workspace>/tmp/<safe-author-name>-candidates.csv"
首次运行会生成二维码,用户扫码确认后,登录态保存到 ~/.cache/wechat-article-archive/session.json。
3. 抓取正文与图片
🔴 CHECKPOINT → 🛑 STOP — 抓取启动前确认:候选列表是否已去重?是否只包含 mp.weixin.qq.com 公开 URL?author_root 目录是否已创建且不与已有公众号冲突?采集延迟和并发设置是否合理(串行 + 2s 延迟)?
🛑 STOP 验证项:
- 候选列表已去重(
biz + mid + idx + sn+ 规范化 URL) - 所有候选 URL 都是
mp.weixin.qq.com公开链接 -
author_root目录已创建、无biz冲突 - 延迟=2s、并发=串行(不可调高)
python3 scripts/collect_articles.py \
--author-root "<author_root>" \
--candidate-csv "<candidate_csv>" \
--limit <N> \
--workers 1 \
--image-workers 2 \
--article-delay 2 \
--resume
- 只接受
mp.weixin.qq.com公开文章 URL - 正文容器优先
#js_content,其次.rich_media_content - 图片按正文顺序下载到
images/image-01.<ext> - 默认串行采集,请求间隔 2 秒
- 正文和图片请求默认失败重试 2 次并指数退避
Markdown 顶部元数据:
# 文章标题
- 公众号:<名称>
- 原文链接:<url>
- 发布时间:<YYYY-MM-DD HH:mm:ss 或未知>
- 采集来源:<source_type>
---
4. 生成文章清单
<博主>-文章清单.csv 使用 UTF-8 BOM,至少包含:
index, title, url, publish_time, error, author_name, article_dir, markdown_file
5. 分析编排(可选)
🔴 CHECKPOINT → 🛑 STOP — 方法论分析启动前确认:用户是否明确要求分析作者方法论?是否已归档 ≥3 篇文章(不足则跳过分析)?HTML 看板和飞书同步是否需关闭(默认开启)?
🛑 STOP 验证项:
- 用户明确要求分析方法论
- 已归档文章数 ≥3 篇(<3 跳过分析)
- 用户未明确要求"不生成 HTML"
- 用户未明确要求"不同步飞书"
用户要求方法论分析时,调用 author-methodology-analysis,传入:
input_dir = <author_root>/articlesoutput_dir = <author_root>author_namearticle_list = <author_root>/<safe-author-name>-文章清单.csvgenerate_html = true(除非用户明确关闭)sync_lark = true(除非用户明确关闭)
6. 校验与打包
🔴 CHECKPOINT → 🛑 STOP — 打包前确认:所有文章正文是否已抓取完成?图片是否已本地化且引用正确?CSV 是否 UTF-8 BOM 格式?是否已排除不可访问的候选?
🛑 STOP 验证项:
-
validate_archive.py校验通过 - CSV 文件是 UTF-8 BOM 编码
- 所有图片本地化成功(或标记为
[图片获取失败]) - 不可访问的候选已从 CSV 排除
python3 scripts/validate_archive.py "<author_root>"
python3 scripts/package_archive.py "<author_root>"
python3 scripts/validate_archive.py "<author_root>" --zip "<zip_path>"
失败边界
- 微信公众平台历史列表会话失效:删除本地缓存并提示重新扫码
- 搜狗或其他跳转触发验证码:立即停止该路径
- 入口文章不可公开访问:说明限制并请求可访问链接
- 不可访问的搜索结果不得进入最终包
- 转载内容不得标记为公众号原文
失败模式与恢复
| 触发条件 | 症状 | 一线修复 | 仍失败兜底 |
|---|---|---|---|
| QR 扫码超时 | discover_account_articles.py 超时退出(exit≠0) |
删除 wechat-login-qr.jpg 和本地缓存,用 --login-timeout 300 重新运行 |
改用公开搜索/合集/内链等无需扫码的来源 |
| Session 缓存被 kill 丢失 | 进程被 kill 后 ~/.cache/wechat-article-archive/session.json 不存在 |
确认进程正常 exit 0 退出(非 kill),session 只在正常退出时写入 | 重新扫码获取新 session |
| 文章页面触发验证码 | collect_articles.py 返回空正文或验证码页面 |
立即停止该 URL 的请求,标记为不可访问 | 跳过该文章,继续采集其他可访问文章 |
| 公众号名称精确匹配失败 | discover 脚本输出 "未找到匹配的公众号" |
用 --fakeid 精确选择(从候选列表中获取) |
让用户提供该公众号的 biz 或直接文章链接 |
| 图片下载失败 | images/ 目录下图片缺失或 404 |
重试 2 次(脚本默认),检查图片 URL 是否过期 | 标记图片失败数,正文中用 [图片获取失败] 占位 |
| 候选列表为空 | collect_articles.py 收到空 CSV |
检查 discover 脚本是否正常完成,尝试不同来源优先级 |
让用户提供更多公开文章链接,手动构建候选列表 |
| 微信改版导致选择器失效 | #js_content / .rich_media_content 均无匹配 |
检查页面源码是否变更,尝试通用内容选择器 | 用 firecrawl_scrape 替代直接抓取,等待脚本更新 |
最终回复
简要报告:实际归档数与失败数、来源范围、发布时间未知数、图片失败数、校验结果、方法论/HTML/飞书完成情况、ZIP 本地绝对路径。
资源
资源
⚠️ Python 脚本需从上游仓库安装到技能 scripts/ 目录:
git clone https://github.com/freestylefly/wechat-article-archive-skill.git /tmp/waa && cp /tmp/waa/scripts/*.py <skill_dir>/scripts/。依赖requests+lxml,需在 venv 中可用。
本技能 Python 脚本源自 freestylefly/wechat-article-archive-skill:
scripts/discover_account_articles.py— 扫码登录微信公众平台,生成候选列表scripts/collect_articles.py— 抓取正文、转换 Markdown、本地化图片scripts/validate_archive.py— 校验目录、CSV、Markdown、图片引用和 ZIPscripts/package_archive.py— 以 UTF-8 文件名创建 ZIPscripts/archive_common.py— 共享 CSV Schema、安全路径规则
QR 码登录工作流 ★ (2026-06-18 实战验证)
首次采集某个公众号必须扫码。不能在 foreground 直接跑(用户看不到终端 stdout 里的 QR 码文本),必须按以下流程操作(已验证四次后成功):
1. 用 --login-timeout 300 后台启动(默认 180s 不够)
terminal(background=true, notify_on_complete=true,
"python3 -u scripts/discover_account_articles.py
--account \"<名称>\" --limit <N> --login-timeout 300
--output <csv_path>")
2. 等 3 秒让 QR 文件写入磁盘
sleep 3 && ls <skill_dir>/wechat-login-qr.jpg
3. 立刻将 QR 码文件发到飞书(用户手机在飞书里可以直接长按识别)
send_message("📱 微信扫码(5分钟内有效)
MEDIA:<skill_dir>/wechat-login-qr.jpg", target="feishu")
4. 等用户扫码 → 监听 notify_on_complete 通知
关键约束:
- QR 文件由脚本写入磁盘(
<cwd>/wechat-login-qr.jpg),与 stdout 输出独立——所以即使 background 缓冲了 stdout,QR 文件仍然可读 - Session 缓存路径:
~/.cache/wechat-article-archive/session.json(权限 0600) - 进程被 kill 时 session 不会保存。正常 exit code 0 退出后 session 自动缓存
- 缓存后同号下次不再需要扫码,直接复用
QR 码登录工作流 ★
首次采集新公众号必须扫码。正确流程(已验证):
1. 后台启动 discover 脚本(300s 超时)
terminal(background=true, notify_on_complete=true,
"python3 -u scripts/discover_account_articles.py
--account \"<名称>\" --limit <N> --login-timeout 300
--output <path>")
2. 等 3 秒让 QR 文件生成
sleep 3 && ls wechat-login-qr.jpg
3. 立刻将 QR 码发到飞书
send_message("📱 微信扫码(5分钟内有效)
MEDIA:<skill_dir>/wechat-login-qr.jpg", target="feishu")
4. 等用户扫码 → process poll 或等 notify_on_complete
为什么不能 foreground 直接跑:foreground 模式下 QR 码打印到终端 stdout,但用户看不到终端输出。必须发到飞书。
为什么不能后台跑完再发 QR:后台 Python 输出被缓冲,process poll 始终返回空。QR 文件是独立写入磁盘的,可以检测到。
关键参数:--login-timeout 300(默认 180s 不够——从发 QR 到用户看到并扫码,180s 很紧)
常见陷阱
- 扫码人未就位 — 首次采集新公众号需要手机扫码。跑脚本前确认扫码人可用
- QR 码超时(最高频陷阱) — 默认
--login-timeout 180偏紧。从生成 QR → 发飞书 → 用户打开飞书 → 长按识别,180s 经常不够。必须用--login-timeout 300 - Foreground 跑 QR 登录 — 脚本在 foreground 会打印 QR 文本到 stdout 然后阻塞等待。用户看不到终端输出。必须用 background 模式 + 检测 QR 文件写入 + 发飞书
- Background Python 缓冲导致空 output —
process poll对 background Python 进程始终返回空。不要依赖 poll 来判断 QR 是否生成——用ls wechat-login-qr.jpg检测文件 - Session 缓存被 kill 丢失 — 如果进程被 kill(非正常 exit 0),session 不会写入
~/.cache/wechat-article-archive/session.json。必须等进程正常退出后 session 才持久化 - 微信改版导致脚本失效 — 脚本有降级策略(公开搜索/合集),非完全依赖后台接口
- 高频采集触发风控 — 默认串行+2s延迟,不建议调高并发
- 公众号名称精确匹配失败 — 用
--fakeid精确选择(从候选列表中获取)+ 检测 QR 文件写入 + 发飞书** - Background Python 缓冲导致空 output —
process poll对 background Python 进程始终返回空。不要依赖 poll 来判断 QR 是否生成——用ls wechat-login-qr.jpg检测文件 - Session 缓存被 kill 丢失 — 如果进程被 kill(非正常 exit 0),session 不会写入
~/.cache/wechat-article-archive/session.json。必须等进程正常退出后 session 才持久化 - 微信改版导致脚本失效 — 脚本有降级策略(公开搜索/合集),非完全依赖后台接口
- 高频采集触发风控 — 默认串行+2s延迟,不建议调高并发
- 公众号名称精确匹配失败 — 用
--fakeid精确选择(从候选列表中获取)
验证清单
- 入口文章可公开访问
- 公众号名称和 biz 已确认
- 输出目录结构符合契约
- 文章清单 CSV UTF-8 BOM 格式正确
- Markdown 元数据完整
- 图片本地化无失效引用
- ZIP 二次校验通过
- 若分析:方法论报告/HTML/飞书均存在
⛔ 反例与禁止
- ❌ 采集需要登录/付费/验证码的内容 — 只采集公开可访问文章,遇到验证码立即停止
- ❌ 在 foreground 跑 QR 登录 — 用户看不到终端 stdout,必须用 background 模式 + 发飞书 QR
- ❌ QR 扫码用默认 180s 超时 — 从发 QR 到用户扫码经常超过 180s,必须用
--login-timeout 300 - ❌ 并发采集或缩短延迟 — 默认串行 + 2s 延迟,调高并发或缩短延迟会触发风控
- ❌ 依赖
process poll判断 QR 是否生成 — background Python 输出被缓冲,用ls wechat-login-qr.jpg检测文件 - ❌ kill 进程后期望 session 可用 — session 只在正常 exit 0 时写入,kill 会导致缓存丢失
- ❌ 伪造"最近 N 篇"或"完整历史" — 公开来源不足时交付实际数量,不虚构