- name
- wechat-article-archive
- description
- 从用户提供的公开微信公众号文章链接出发,采集可公开访问的文章正文与图片,按公众号隔离保存为本地 Markdown 归档,生成文章清单,并按需调用 author-methodology-analysis 生成方法论报告与文案框架;进入分析流程后默认自动生成 HTML 看板并同步飞书,最后校验并打包 ZIP。适用于“采集公众号最近 N 篇”“公众号文章带图 Markdown 归档”“按之前一样整理文章”“归档后分析作者方法论”等请求;不用于绕过登录、验证码、反爬或获取私密内容。
# 微信公众号文章归档
## 默认行为
- 只采集用户有权访问的内容,不绕过验证码或风控。正文抓取不使用 Cookie;精确获取公众号历史列表时可由用户扫码登录微信公众平台,登录态仅保存在用户本机缓存。
- 用户可自定义采集篇数;未指定时默认目标为最近 50 篇。
- 默认生成文章归档、`<博主>-文章清单.csv` 和 ZIP。
- 仅当用户要求分析作者方法论时,调用 `author-methodology-analysis`。
- 进入方法论分析流程后,默认自动生成 HTML 看板并同步飞书。
- 只有用户明确要求“不生成 HTML”或“不同步飞书”时才关闭对应产物。
- 公开来源不足时交付实际数量,不伪造“最近 N 篇”或“完整历史”。
## 唯一输出契约
先识别公众号名称和 `biz`,再确定:
```text
author_root = <workspace>/output/<safe-author-name>/
```
若同名目录已属于其他 `biz`,使用 `<safe-author-name>-<biz-tail>`。微信内部标识、时间置信度、时间线完整性、采集来源和内部状态只用于采集与校验,不写入最终文章清单。
最终结构:
```text
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>-竞品标题样本.json # 提供竞品样本时生成
<safe-author-name>-文章归档.zip
```
ZIP 内必须保留 `<safe-author-name>/` 顶层目录,内部结构与上面一致,但不得把 ZIP 自身打入 ZIP。
每篇文章目录只允许包含一个同名 Markdown 文件和 `images/`。文件和目录名需移除 `/\\:*?"<>|`,目录名前保留两位数字序号。
## 工作流
### 1. 解析入口并锁定身份
从入口文章提取:
- 标题:`#activity-name`、`msg_title` 或 `<title>`
- 公众号名:`#js_name` 或 `nickname`
- `__biz`/`biz`、`mid`/`appmsgid`、`idx`、`sn`
- 发布时间、合集信息及页面显式文章链接
创建或核验 `author_root`。已有目录只有在 `biz` 相同,或 `biz` 缺失但公众号名和多条文章 URL 均一致时才能增量复用。
### 2. 锁定候选列表
候选字段至少包括:
```text
title,url,publish_time,source_type,accessible,biz,mid,idx,sn
```
来源优先级:
1. 用户扫码授权后的微信公众平台文章历史列表
2. 无需登录即可分页的公众号公开历史入口
3. 公开合集/专辑
4. 当前文章页显式内链
5. 最多两轮公开搜索补充
6. 经身份核验的本地既有归档
候选按 `biz + mid + idx + sn` 和规范化 URL 去重。同名但文章键不同的文章必须保留。
用户要求“最近 N 篇”“完整采集”或只提供公众号名称时,优先使用确定性历史列表脚本。`N` 为用户指定数量;未指定时取 `50`:
```bash
python3 scripts/discover_account_articles.py \
--account "<公众号精确名称>" \
--limit <N> \
--output "<workspace>/tmp/<safe-author-name>-candidates.csv"
```
首次运行会生成二维码,用户扫码确认后,登录态默认保存到 `~/.cache/wechat-article-archive/session.json`,文件权限设为仅当前用户可读写。若搜索结果重名,脚本拒绝猜测并列出候选,此时用 `--fakeid <fakeid>` 精确选择。会话失效时脚本删除缓存,重新运行并扫码即可。
历史列表脚本按后台返回顺序分页,提取标题、链接、明确发布时间和稳定账号身份,并输出候选 CSV。只有脚本返回 `timeline_complete: true`,且正文采集无失败、无未知发布时间时,正文采集命令才可传 `--timeline-complete`。
不需要或无法扫码时,使用搜索、公开历史入口或合集获得 URL,将候选写为 CSV,至少包含 `url`,可选包含 `source_type`、`publish_time`、`time_confidence`。当前文章页显式内链可由采集脚本自动发现。公开搜索和需浏览器观察的历史入口仍由可用搜索/浏览器工具完成,不在脚本中绕过访问限制。
发布时间可信来源按优先级为:
1. 页面 DOM 中明确的发布时间
2. 页面脚本中的 `ct`、`publish_time`、`create_time` 等 Unix 时间戳
3. 合集或历史接口明确返回的发布时间字段
4. 候选来源中可追溯的发布时间
`scene` 等访问场景参数不得作为发布时间。无法确认时写 `未知`。只有来源覆盖时间线且发布时间可靠时才能称为“最近 N 篇”;否则表述为“可公开采集的 N 篇”。
### 3. 抓取正文与图片
新归档优先运行确定性采集脚本:
```bash
python3 -c "import requests, lxml"
python3 scripts/collect_articles.py \
--author-root "<author_root>" \
--candidate-csv "<candidate_csv>" \
--limit <N> \
--workers 1 \
--image-workers 2 \
--article-delay 2 \
--resume
```
已有完整候选 CSV 时不要使用 `--discover-links`,避免把推荐文章或其他公众号链接混入完整时间线。只有从少量入口文章探索公开内链时才开启该参数。
也可直接在命令末尾传入一个或多个公开微信文章 URL。目标目录已存在时:
- 默认拒绝操作。
- `--resume` 复用已有成功文章,只重试缺失或失败文章。
- `--replace` 不复用现有归档,重新构建。
- `--no-cache` 强制跳过外部正文和图片缓存,适合确认文章内容已经更新时使用。
所有模式都采用临时目录完整构建、校验和旁路备份,失败时保留原归档。正文与图片缓存默认位于 `~/.cache/wechat-article-archive/content/`,不进入 ZIP。
只有候选来源已被确认覆盖完整时间线时才传 `--timeline-complete`。若存在未知发布时间或抓取失败,脚本会自动降级为 `false`。
脚本执行以下操作:
- 只接受 `mp.weixin.qq.com` 公开文章 URL。
- 正文容器优先 `#js_content`,其次 `.rich_media_content`。
- 保留标题、小标题、段落、列表、引用和正文链接。
- 图片按正文顺序下载到 `images/image-01.<ext>`,仅允许已知微信图片域名,并限制单图 20 MB。
- 图片下载使用文章 URL 作为 Referer;相同 URL 只下载一次,默认 2 张并发,失败时删除远程引用并记录 `image_failures`。
- 文章默认串行采集,并在请求启动之间等待 2 秒;可用 `--workers`、`--image-workers` 和 `--article-delay` 调整。为降低环境验证风险,不建议批量任务将文章并发调高。
- 正文、图片和后台列表请求默认失败重试 2 次并指数退避,可用 `--retries` 调整。
- Markdown 转换保留标题、段落、列表、引用、链接、代码块和表格;视频、音频及嵌入内容保留为来源链接或明确占位。
- 自动生成文章目录、Markdown 和 `<博主>-文章清单.csv`。
- `--limit` 接受任意正整数;省略时默认 `50`。
Markdown 顶部必须包含:
```markdown
# 文章标题
- 公众号:<名称>
- 公众号标识:<biz 或未知>
- 原文链接:<url>
- 发布时间:<YYYY-MM-DD HH:mm:ss 或未知>
- 采集来源:<source_type>
---
```
### 4. 生成文章清单
`<博主>-文章清单.csv` 必须使用 UTF-8 BOM,至少包含:
```text
index,title,url,publish_time,error,author_name,article_dir,markdown_file
```
- `identity_key`、`biz`、`mid`、`idx`、`sn`、`time_confidence`、`timeline_complete`、`source_type`、`status` 仅作为候选发现和内部校验字段,不得出现在最终交付清单。
- `article_dir` 和 `markdown_file` 均有值表示归档成功;均为空且 `error` 有值表示采集失败。
- `markdown_file` 显式记录清洗、截断后的文件名,避免校验器重新猜测文件名。
- 失败行必须填写 `error`,且不得引用文章目录。
- ZIP 只包含具有有效文章目录和 Markdown 文件的文章。
### 5. 分析编排
用户要求方法论分析时,调用 `author-methodology-analysis`,传入:
- `input_dir = <author_root>/articles`
- `output_dir = <author_root>`
- `author_name`
- `article_list = <author_root>/<safe-author-name>-文章清单.csv`
- `generate_html = true`,除非用户明确关闭
- `sync_lark = true`,除非用户明确关闭
归档 skill 不重复生成分析报告,也不直接依赖不存在的第三方文案 skill。
### 6. 校验与打包
先运行:
```bash
python3 scripts/validate_archive.py "<author_root>"
```
校验器必须通过以下检查:
- CSV 索引连续、URL 和文章键不重复、身份一致。
- `article_dir` 不能是绝对路径、包含 `..` 或逃逸 `articles/`。
- 归档内禁止符号链接。
- Markdown 元数据必须与 CSV 一致。
- Markdown、HTML 和引用式图片不得指向远程或越界路径。
- Markdown 正文不得为空或包含验证页、频控页、删除页等异常页面标记;过短正文和转换保留率异常计入质量统计。
- 根目录不得包含未声明文件。
校验通过后使用配套脚本重新创建 ZIP,禁止调用可能破坏中文文件名的系统 `zip`,也禁止增量覆盖旧 ZIP:
```bash
python3 scripts/package_archive.py "<author_root>"
```
打包后再运行:
```bash
python3 scripts/validate_archive.py "<author_root>" --zip "<zip_path>"
```
打包器只加入 CSV 中 `archived` 文章和六个声明的分析产物,不会递归打包未知文件。ZIP 二次校验必须检查文件白名单、额外条目、缺失条目和 CRC。
若进入分析流程,额外校验方法论报告、文案框架、分析数据、文章特征和 HTML 均存在。除非用户明确关闭飞书,否则校验飞书文档可读取且包含主报告关键章节;飞书成功后还应存在 `<博主>-飞书同步.json`。飞书失败不得阻塞本地归档和 ZIP,但必须在最终结果中说明。
## 失败边界
- `profile_ext` 返回 `no session`、登录页或验证页:记录一次后立即降级。
- 微信公众平台历史列表会话失效:删除本地会话缓存并提示重新扫码,不循环重试。
- 搜狗或其他跳转触发验证码:立即停止该路径。
- 入口文章本身不可公开访问:说明限制并请求可访问链接。
- 不可访问的搜索结果不得进入最终包。
- 转载内容不得标记为公众号原文。
## 最终回复
简要报告:
- 实际归档数与失败数
- 来源范围,以及是否能严格称为“最近 N 篇”
- 发布时间未知数、图片失败数、过短正文/转换质量警告数和校验结果
- 方法论、HTML、飞书是否完成;若被用户关闭或执行失败,说明原因
- 使用普通可点击的本地绝对路径链接返回 ZIP,不使用 `local-file://`
## 资源
- `scripts/validate_archive.py`:校验目录、CSV、Markdown、图片引用和 ZIP。
- `scripts/package_archive.py`:以 UTF-8 文件名重新创建 ZIP,并排除 ZIP 自身。
- `scripts/collect_articles.py`:从公开微信 URL/候选 CSV 抓取正文、转换 Markdown、本地化图片并生成清单。
- `scripts/discover_account_articles.py`:扫码登录微信公众平台,精确搜索公众号并分页生成最近 N 篇候选列表,默认 50 篇。
- `scripts/archive_common.py`:共享 CSV Schema、安全路径、命名和打包白名单规则。
View on GitHub