一条命令,把你自己运营的微信公众号的全部历史文章,完整归档为本地 HTML + PDF + Excel 索引。
公众号没有官方的批量导出功能,文章一旦多起来,手动一篇篇另存几乎不可能。WeMP-Keeper 面向公众号管理员,登录一次(扫码),即可自动抓取并归档名下公众号的所有文章,离线可读、格式不崩。
- 真实浏览器渲染:用 Playwright 加载文章后再导出,彻底解决微信正文
visibility:hidden、图片懒加载导致的「HTML/PDF 空白错乱」问题。 - 三种产物一次到位:每篇文章 → 独立 HTML(图片本地化、CSS 内联,断网也能看)+ A4 PDF;全部文章 → 一份 Excel 索引(标题 + 发布时间,点击跳转 PDF)。
- 官方后台接口:走管理员后台的「发表记录」接口,直接列出本号全部文章,无需 fakeid、无需第三方平台。
- 分阶段限流:列表接口低并发防风控,文章 CDN 高并发提速;命中反爬验证页能自动识别并降速重抓。
- 断点续传:随时中断、重跑即续,已完成的不重做。
- 内置质检:一条命令扫出验证页 / 缺失 / 异常产物,确保归档完整。
output/
├── html/
│ └── 2025-09-01_新生开学典礼圆满举行_551234/
│ ├── index.html # 离线可读,含本地图片
│ └── images/ # 正文所有图片
├── pdf/
│ └── 2025-09-01_新生开学典礼圆满举行_551234.pdf
└── index.xlsx # 标题 + 发布时间,标题列超链接到 PDF
抓公众号文章有几个绕不开的坑,WeMP-Keeper 的设计都是为了解决它们:
| 难点 | 朴素做法的问题 | 本项目的方案 |
|---|---|---|
| 拿到全部文章列表 | Sogou 搜索不全、第三方平台要钱 | 直接调管理员后台 cgi-bin/appmsgpublish?sub=list&f=json,返回本号全部发表记录(含阅读/点赞数),无需 fakeid |
| 正文导出后空白 | 正文容器 #js_content 默认 visibility:hidden,靠 JS 才显示;直接抓原始 HTML 删掉 JS → 正文永久隐藏 |
用 Playwright 真实渲染,等 JS 跑完、滚动触发懒加载,再导出 |
| 图片不显示 / 离线打不开 | 图片用 data-src 懒加载且有防盗链 |
渲染后下载所有图片到本地、改写 src、内联外部 CSS |
| 被风控(环境异常验证页) | 高并发抓后台接口立刻触发 ret=200013 锁号 |
列表接口单/双 worker + 随机间隔;文章走公网 CDN 才高并发 |
关键认知:列表接口(
mp.weixin.qq.com/cgi-bin/...)对并发极敏感,必须温柔;而文章详情页(mp.weixin.qq.com/s/...)是 CDN 缓存的公网资源,可以放心并发。
数据结构是三层嵌套 JSON:publish_page(字符串) → publish_list[] → 每项 publish_info(字符串) → appmsg_info[](一次群发可含多篇文章)。fetch_list.py 负责把它拍平成一篇一条。
需要 Python 3.9+。
git clone https://github.com/handsomeZR-netizen/WeMP-Keeper.git
cd WeMP-Keeper
pip install -r requirements.txt
playwright install chromium # 下载无头浏览器内核Windows 提示:脚本已强制 UTF-8 输出;若直接在 cmd 看到乱码,设
set PYTHONIOENCODING=utf-8即可。
python src/login.py弹出浏览器 → 用管理员微信扫码登录公众号后台 → 脚本自动提取 token 和完整 Cookie(含 HttpOnly 鉴权字段),存入 state/biz_info.json。
为什么能全自动:Playwright 在浏览器协议层读 Cookie,能拿到
slave_sid、slave_user等 HttpOnly 鉴权 Cookie——这是网页 JSdocument.cookie拿不到的,也是手动复制最容易漏的部分。
python src/main.py依次执行:拉列表 → 渲染 HTML+PDF → 生成 Excel。产物全在 output/。
python src/check_quality.py扫描所有产物,报告验证页 / 缺失 / 异常,并把需重抓的 URL 写入 state/need_redo.json。
编辑 config/settings.json:
| 字段 | 默认 | 说明 |
|---|---|---|
cutoff_date |
"" |
空 = 抓全部历史;填 "2025-06-01" 则只抓该日(含)之后的文章 |
page_size |
20 |
列表接口每页条数(最大 20) |
request_interval_min / max |
3 / 6 |
列表翻页的随机间隔(秒) |
rest_every_pages |
10 |
每翻 N 页长休息一次 |
rest_seconds |
30 |
长休息秒数 |
save_workers |
3 |
HTML+PDF 渲染并发数(2-3 稳;调高可能触发风控) |
所有步骤幂等,断了重跑即续:
python src/main.py --only=list # 只拉文章列表 → state/articles.jsonl
python src/main.py --only=save # 只渲染 HTML+PDF(已存在的自动跳过)
python src/main.py --only=excel # 只生成 Excel
python src/main.py --skip-list # 跳过列表,直接用现有 articles.jsonl 往下走
python src/save_article.py --workers=1 # 命中风控后,单 worker 温柔重抓
python src/save_article.py --limit=3 # 先跑 3 篇冒烟遇到验证页怎么办:跑 check_quality.py → 删掉对应坏产物(或直接删 output/ 里那几个目录/PDF + 从 state/progress.json 移除)→ python src/save_article.py --workers=1 重抓。
WeMP-Keeper/
├── src/
│ ├── login.py # 扫码登录,自动抓 token + 完整 Cookie
│ ├── fetch_list.py # 调 appmsgpublish 分页拉全部文章元数据
│ ├── save_article.py # Playwright 实时渲染 → HTML + PDF(二合一)
│ ├── build_excel.py # 生成标题+时间的 Excel 索引
│ ├── check_quality.py # 质检:揪出验证页/缺失/异常
│ ├── probe.py # 调试用:探测接口真实返回结构
│ ├── main.py # 主编排
│ └── utils.py # 路径、配置、文件名清洗、重试
├── config/settings.json # 配置
├── state/ # 凭证 & 中间数据(gitignore,不上传)
├── output/ # 最终产物(gitignore,不上传)
└── requirements.txt
Q: 提示 ret=200003 invalid session / ret=-3?
Cookie 失效了(有效期约 1-2 小时)。重跑 python src/login.py 即可。
Q: 抓到的是「环境异常,完成验证后即可继续访问」?
触发了反爬。把 save_workers 调小到 1-2,或用 python src/save_article.py --workers=1 重抓。
Q: 能抓别人的公众号吗? 本工具基于你自己的管理员后台,只能归档你有管理权限的公众号。这也是它稳定、完整的原因。
Q: 文章里的视频 / 音频会保存吗? 不会,仅保存图文与图片。视频是外链流媒体,PDF/HTML 中保留占位。
- 本工具仅用于公众号运营者备份自己的合法内容,请勿用于抓取无权限的账号或侵犯他人版权。
- 使用即表示你已获得相应内容的处置授权,并自行承担因使用产生的一切责任。
- 请遵守微信平台的用户协议与相关法律法规,合理控制请求频率。
state/biz_info.json含登录 Cookie,切勿分享或提交到任何公开仓库(本项目已默认 gitignore)。
MIT © 2026 handsomeZR-netizen