Summary
批量归档用户自己管理的微信公众号已发文章到 Obsidian vault。支持官方 API、公众号后台接口、登录态 Cookie 三条路线,并按 url 去重。Triggers:「同步我的公众号」「批量拉取我公众号历史文章」「批量归档我的公众号」「导入公众号已发文章」「clip 我的公众号」
soia-team/soia-open-pkm-vault-skills · Archived
批量归档用户自己管理的微信?
npx skills add soia-team/soia-open-pkm-vault-skills --skill soia-pkm-clip-wechat-account
批量归档用户自己管理的微信公众号已发文章到 Obsidian vault。支持官方 API、公众号后台接口、登录态 Cookie 三条路线,并按 url 去重。Triggers:「同步我的公众号」「批量拉取我公众号历史文章」「批量归档我的公众号」「导入公众号已发文章」「clip 我的公众号」
This repository is archived — consider an actively maintained alternative.
把文章、提纲或主题转换为以可编辑 PPTX 为正式母版的演示媒体?
5 installs将 Obsidian 文章库按?
5 installs为 vault 长文或论文生成独立 AI 解读,帮助判断是否值得深挖,且不改原文或代写用户观点。触发:「解…
5 installs将单条 X/Twitter 推文、thread 或 Article 归档到 Obsidian vault。触发:「归档这条 X」「clip 这条…
5 installsRelated neighbors and high-traction skills in the same topics — useful to compare before installing.
Build custom functionality that merchants can install at defined points on the Order index, Ord…
8.6K installsResearch a company or person and get actionable sales intel. Works standalone with web search, …
3K installsConfigures AWS Resilience Hub v2 for multi-account resilience management across an AWS Organiza…
2.3K installsStrategic account planning and execution for enterprise deals. Use when planning complex sales …
1.6K installsVerify that browser profiles are actually isolated from one another instead of assuming it - co…
47.1K installs批量账号授权工作流:编排 account list + staff list + api 调用完成批量授权店铺给员工。适用于新员…
4.9K installsOther skills from soia-team/soia-open-pkm-vault-skills · top by installs.
npx skills add soia-team/soia-open-pkm-vault-skills
Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.
main
Parsed from SKILL.md frontmatter.
Files included with this skill beyond the listing page.
SKILL.md
25,453 B
SUMMARY.md
384 B
clip 家族的公众号批量成员:和 soia-pkm-clip-wechat-article(单篇、贴 URL 归档)不同,这个 skill 面向"把我自己公众号的历史文章一次性拉进 vault"的批量场景。只用于归档你自己管理的公众号——不是通用的公众号爬虫。
批量归档用户自己管理的微信公众号已发文章到 Obsidian vault。支持官方 API、公众号后台接口、登录态 Cookie 三条路线,并按 url 去重
| 客户想要 | 技能会做 | 客户能看到 |
|---|---|---|
| 完成本技能覆盖的工作 | 读取用户请求、必要上下文和本技能正文流程,执行最小可靠步骤 | 客户会看到 Obsidian/vault 文件变更、终端日志、生成产物路径和最终回执。 |
| 缺少依赖、权限、配置或 key | 停止需要外部状态的动作,明确指出缺什么 | 安装命令、申请地址、配置路径或需要客户确认的问题 |
| 执行完成 | 汇总成功、跳过、失败、文件变更和验证结果 | 一段可复制进工单/日志的完成回执 |
安装(推荐:装整个领域插件,一次装好本仓全部技能):
claude plugin marketplace add soia-team/soia-open-skills
claude plugin install soia-pkm-vault@soia
只要这一个技能时,可用 npx 路线。注意技能会落进共享真源 ~/.agents/skills;若同时装了插件,同一技能会出现两份索引且各自漂移,建议二选一:
npx skills add soia-team/soia-open-pkm-vault-skills -g -a '*' -s soia-pkm-clip-wechat-account -y
配置约定:
~/.config/soia-skills/soia-pkm-clip-wechat-account/config.yml
SOIA_PKM_CLIP_WECHAT_ACCOUNT_CONFIG_FILE=<custom-config-path>
# 兼容别名:$SOIA_PKM_CLIP_GZH_CONFIG_FILE(以及原有 $SOIA_PKM_CLIP_GZH_ENV_FILE)
config.yml。config.yml、进程环境或 provider 自己的登录态里,不能写进仓库、vault 正文或日志。WorkBuddy 的装载单位是角色化专家而不是插件,npx skills add -a '*' 覆盖不到它,需要单独安装,见 docs/install/workbuddy.md。
批量抓取只代表 captured;落盘后按生命周期管理技能逐对象完成 organized → MOC/导航 → map → Base,不适用阶段显式写 not_applicable,不能以批量成功数代替闭环证据。
每次执行都要让客户看见过程和结果。最低回执格式:
完成:<一句话说明本次完成了什么>。
日志摘要:
- started: <检查到的输入/配置/依赖,不打印秘密值>
- processed: <数量或范围>
- created/updated: <数量或路径>
- skipped/failed: <数量和原因>
文件变化:
- <绝对路径或“未改动文件”>
验证:
- <运行过的检查、命令或人工核对点>
问题与下一步:
- <缺 key / 缺依赖 / 需要客户确认 / 建议下一条命令;没有则写“无”>
交付顺序:批量归档必须先把文章全部落盘,再统一输出上面的回执,不得反过来;author 靠 --account-name 兜底、content_complete: false 等不确定字段要在回执里显式标注“未核实”,不编造。
| 路 A · 官方 API | 路 C · 公众号后台接口 | 路 B · 登录态 Cookie | |
|---|---|---|---|
| 覆盖范围 | 只有通过草稿箱「发布」发出的文章 | 全部历史,含手动群发的老文 | 全部历史,含手动群发的老文 |
| 账号要求 | 需已认证服务号/订阅号 | 任意公众号,只要你能登录后台 | 任意公众号,只要你能登录后台 |
| 凭据 | AppID/AppSecret,长期有效 + IP 白名单 | token/Cookie,几小时过期 | key/passticket/appmsgtoken,几小时~几天过期 |
| 凭据抓取难度 | 一次性配置(后台生成) | 抓 1 对值(token+Cookie) | 抓 3 个票据 + Cookie,容易漏抓/配错对 |
| 官方程度 | developers.weixin.qq.com 有文档 | 非官方,社区逆向(源码级核对) | 非官方,社区逆向,未见于官方文档 |
| 脚本 | scripts/fetch_api.py |
scripts/fetch_mp.py |
scripts/fetch_cookie.py |
建议:先跑路 A 探路(凭据搭起来一次成本低,长期可复用于增量同步;多数个人未认证号会直接卡在权限门槛,见下)。要读全部历史(含手动群发的老文),优先用路 C——凭据只需抓一对 token/Cookie,比路 B 要配对三个票据更不容易出错,是本 skill 里读自己号历史文章的推荐路线。路 B 保留作为路 C 失效时的备选(例如某天两个接口都改版)。三条路都只在你自己能登录/管理的账号上跑;跑过的账号落地时会按 url 自动去重(互相跳过已归档的文章)。
路 C 内部又分两条子路径,脚本按你传不传 --name/--fakeid 自动选:不传(默认)走 appmsgpublish,读你自己当前登录的号;传了 --name/--fakeid 才走 searchbiz+appmsg,读指定/别人的号——原因见下方路 C 小节。
正式抓正文/写文件前,先用 --dry-run --limit 3 之类的小样本探路,向客户报告本次预计处理的公众号、篇数量级与时间范围;客户确认范围无误后再放开全量抓取。显式调用本 skill、传了 --name/--limit 等参数,都只是推荐输入,不构成跳过这次确认的理由——唯一跳过条件是客户当前这句话明确说"直接抓/不用确认",跳过后要在回执里说明本次沿用的范围假设。
路 B / 路 C 走的是公众号后台非官方接口,存在 ToS 风险。首次使用本 skill,或本节声明版本变化时,必须先展示以下声明并取得客户显式同意才能继续:
本技能部分路线(路 B / 路 C)使用公众号后台非官方接口,非微信官方文档收录,存在 ToS 风险;仅限归档你自己管理的账号,不得用于抓取他人公众号。(声明版本 v1)
{accepted: true, acceptedAt: <ISO 时间戳>, disclaimerVersion: "v1"} 写入私有配置目录下的 consent.json(与 config.yml 同级:~/.config/soia-skills/soia-pkm-clip-wechat-account/consent.json)。disclaimerVersion 匹配当前版本(v1)→ 跳过展示,直接继续,仅在日志里提一句"已确认使用条款(\<acceptedAt\>)"。| 接口 | 方法 | 请求 | 响应关键字段 | 依据 |
|---|---|---|---|---|
| 获取 access_token | GET | /cgi-bin/token?granttype=clientcredential&appid=&secret= |
accesstoken, expiresin |
官方文档(已取到完整请求/响应示例) |
| 获取成功发布列表 | POST | /cgi-bin/freepublish/batchget?accesstoken=,body {offset,count,nocontent} |
totalcount,itemcount,item[].articleid,item[].updatetime,item[].content.newsitem[].{title,author,digest,content,contentsourceurl,thumbmediaid,showcoverpic,needopencomment,onlyfanscancomment,url,is_deleted} |
官方文档(抓取时未能取到完整示例正文,字段名已与微信开放社区多个独立帖子交叉核对一致——建议先用 --dry-run --limit 3 实测校准一次) |
| 获取永久素材列表 | POST | /cgi-bin/material/batchgetmaterial?accesstoken=,body {type:"news",offset,count}(count 官方标注 1-20) |
totalcount,itemcount,item[].mediaid,item[].updatetime,item[].content.newsitem[].{title,thumbmediaid,showcoverpic,author,digest,content,url,contentsource_url} |
官方文档(已取到完整请求/响应示例) |
freepublish/batchget 只返回通过草稿箱「发布」动作发出的文章。只走过"群发"但没点过"发布"的内容、以及草稿箱功能上线前的旧版图文消息,官方目前没有任何 API 能拿到——这不是本 skill 的实现缺陷,是微信开放社区多个帖子交叉确认过的平台限制。material/batchget_material(type=news)返回的是永久图文素材;据社区反馈,一篇文章一旦经草稿箱正式「发布」,可能就从这个素材列表里消失。两个接口是互补关系,取并集也不等于"全部已发布历史"。soia-pkm-publish-wechat-draft 的 draft/add 一样的账号门槛)。WECHATAPPID / WECHATAPPSECRET:获取路径、IP 白名单配置同 soia-pkm-publish-wechat-draft SKILL.md「如何获取并配置微信公众号 AppID / AppSecret」一节,不重复贴——两个 skill 用同一套公众号开发凭据。$SOIAPKMCLIPWECHATACCOUNTCONFIGFILE(兼容别名 $SOIAPKMCLIPGZHCONFIGFILE,以及原有 $SOIAPKMCLIPGZHENVFILE)> ~/.config/soia-skills/soia-pkm-clip-wechat-account/config.yml。配置文件使用 YAML env: 映射,示例见 assets/config.example.yml;秘钥不进 vault、不进这个开源 skill 仓库。python3 scripts/fetch_api.py [--out <vault内相对目录>] [--limit N] [--dry-run] \
[--vault <path>] [--account-name <公众号显示名>] [--force] [--page-size 20]
--dry-run:只拉列表打印,不写文件——首次跑强烈建议先 dry-run,核对 totalcount/itemcount 和拿到的篇数是否符合预期(尤其 freepublish/batchget 字段未 100% 核实到官方示例正文)。--account-name:两个接口都不返回公众号昵称,给文件名/来源信息用;不传则文件名里用"公众号"占位。--force:按 url 已归档的文章默认跳过,加这个才会覆盖重写。复用 mp.weixin.qq.com 后台(作者登录后台,非读者端 profileext)的接口,和路 B 一样能读全部历史(含手动群发的老文),但凭据只需抓一对 token/Cookie,比路 B 要配对三个票据(key/passticket/appmsg_token)更不容易出错。
路 C 内部有两条子路径,scripts/fetch_mp.py 按你是否传 --name/--fakeid 自动切换:
默认(不传 --name/--fakeid) |
传 --name 或 --fakeid |
|
|---|---|---|
| 接口 | appmsgpublish(作者端「发表记录」) |
searchbiz(名→fakeid)+ appmsg?action=list_ex(fakeid→列表) |
| 读的是谁的号 | 你自己当前登录的这个账号,不需要指定名字/fakeid | 指定名称/fakeid 对应的账号(可以是别的、你也管理的号) |
| 适用场景 | 最常见场景:就是想备份自己号 | 你以某账号登录后台,但要读的是另一个你也管理、且知道名称/fakeid 的号 |
| 验证状态 | 已实测:2026-07-09 真实账号跑通,ret=0,total_count=152 |
源码级核对,未见官方文档,标记「待用户实测校准」 |
为什么默认路径不用 searchbiz:searchbiz 是按名字模糊搜索公众号列表,设计给"找到别人的号"用;用自己公众号的名字去搜,经常搜不到自己(返回空列表),不适合当"我已经登录、我就是这个号"的默认路径。appmsgpublish 直接读的是当前登录态对应账号的发表记录,不存在这个问题。
| 接口 | 方法 | 请求 | 响应关键字段 | 依据 |
|---|---|---|---|---|
| appmsgpublish(默认,读自己号) | GET | /cgi-bin/appmsgpublish,params sub=list&begin=<0,20,40,...>&count=20&type=1011&freepublishtype=1&subaction=listex&token=&lang=zhCN&f=json&ajax=1 |
baseresp.ret;publishpage(JSON 字符串,json.loads 后为 {totalcount, publishlist[]});每条 publishlist[].publishinfo(又是一层 JSON 字符串),json.loads 后为 {appmsgex[]},每条 appmsgex[] 含 title/link/update_time/digest |
cv-cat/WechatOAApis — utils/wxutils.py 源码级核对,并于 2026-07-09 由用户在真实账号上实测验证通过 |
| searchbiz(名 → fakeid) | GET | /cgi-bin/searchbiz,params action=searchbiz&begin=0&count=5&query=<公众号名>&token=&lang=zhCN&f=json&ajax=1 |
baseresp.ret,list[].{alias,fakeid,nickname,roundheadimg,servicetype} |
wnma3mz/wechatarticlesspider — ArticlesUrls.py::officialinfo() 与 cv-cat/WechatOAApis — utils/wxutils.py::getfakeidparams() 两个独立仓库参数完全一致 |
| appmsg(fakeid → 文章列表) | GET | /cgi-bin/appmsg,params action=listex&begin=<0,5,10,...>&count=5&fakeid=<fakeid>&type=9&query=&token=&lang=zhCN&f=json&ajax=1 |
baseresp.ret,appmsgcnt,appmsglist[].{aid,appmsgid,cover,digest,itemidx,link,title,updatetime}(不含 author 字段) |
wnma3mz/wechatarticlesspider — ArticlesUrls.py::__getarticlesdata() |
appmsgpublish 和 appmsg?action=listex 不是同一个接口、响应结构和分页步长都不同——应该是 mp 后台新旧两版前端各自调用的接口,脚本里是两套独立实现(listownaccount() vs fetcharticle_list()),不能混用。
appmsgpublish 虽然已实测通过,同样不是官方文档收录的接口。--name 命中后会打印 nickname/fakeid 并提示确认,不是你自己的号就 Ctrl+C 中断;默认路径读的就是当前登录账号本身,不存在认错号的问题。base_resp.ret 会返回非 0(脚本已按下表给出针对性提示,两条子路径共用同一套提示,社区逆向交叉核对,未见官方文档,仍属「待用户实测校准」):ret |
含义 | 处理 |
|---|---|---|
200003 |
invalid session | token/cookie 已过期,重新登录后台抓包替换 |
200013 |
freq control | 触发限流,社区报告通常需要等待较长时间,别立刻重跑 |
200040 |
invalid csrf token | token 和 Cookie 疑似不是同一次登录会话抓的 |
author 用 --account-name / WECHATACCOUNTNAME 兜底,不保证等于文章真实署名作者,需要精确作者请人工核对原文页。--sleep 3 秒;社区报告即使 5 秒/页的节流,抓到近千篇量级仍可能触发 freq control,批量抓取整年历史建议配合 --limit 分批跑。登录 mp.weixin.qq.com 后台,随便打开一篇文章编辑页或文章列表页,F12 打开浏览器开发者工具「网络」面板,从地址栏 URL 里复制 token 参数、从请求头复制完整 Cookie 字符串。按 assets/config.example.yml 填进私有 config.yml 的 env.WECHATMPTOKEN / env.WECHATMPCOOKIE。
⚠️ 注意 token 要取地址栏 URL 里的那个(数字串),不是「网络」面板里某个请求参数名叫 appmsg_token 的那个票据——两者是不同的票据,appmsgpublish 认的是地址栏 token。
# 默认:不传 --name/--fakeid,读你自己当前登录的账号(appmsgpublish,推荐)
python3 scripts/fetch_mp.py [--out <目录>] [--limit N] [--dry-run] \
[--vault <path>] [--account-name <显示名>] [--force] [--sleep 3] [--page-size 20]
# 传 --name 或 --fakeid:读指定/别人的号(searchbiz + appmsg?action=list_ex)
python3 scripts/fetch_mp.py --name <公众号名> [--out <目录>] [--limit N] [--dry-run] \
[--vault <path>] [--account-name <显示名>] [--force] [--sleep 3] [--page-size 5]
--name/--fakeid(默认,推荐):读你自己当前登录的账号,走 appmsgpublish,--page-size 默认 20。--name 与 --fakeid(二选一):走 searchbiz+appmsg?action=list_ex,--page-size 默认 5;--name 会先跑 searchbiz 搜索、取第一个匹配结果;已知 fakeid 时直接传 --fakeid 跳过搜索这一步更稳。--dry-run:只翻页拉列表打印标题+链接,不抓正文、不写文件——比路 B 的 dry-run 更快(路 B 的 dry-run 仍会抓完全部正文),首次跑建议先 dry-run 核对篇数和标题是否符合预期。--page-size 不传时按走的子路径自动取默认值(自己号 20 / 指定号 5),两个都是已验证/社区实测的稳定值,别调大。link 后,脚本直接 GET 文章公开页面解析 #js_content(和路 B / soia-pkm-clip-wechat-article 单篇归档同一套正文抓取思路,不需要额外 cookie)。GET https://mp.weixin.qq.com/mp/profile_ext
?action=getmsg&__biz=<biz>&f=json&offset=<offset>&count=10&is_ok=1
&scene=124&uin=<uin,通常固定777>&key=<key>&pass_ticket=<pass_ticket>
&wxtoken=&appmsg_token=<appmsg_token>&x5=0
Header: Cookie: <登录态 Cookie>
响应:{"ret":0,"errmsg":"ok","msg_count":N,"can_msg_continue":0|1,
"general_msg_list":"<JSON 字符串,需二次 json.loads>","next_offset":N,...}
general_msg_list 解析后:
{"list":[{"comm_msg_info":{"datetime":<unix ts>},
"app_msg_ext_info":{"title","author","content_url","is_multi",
"multi_app_msg_item_list":[同字段的合集子项]}}]}
依据来源:多篇独立技术博客(博客园/CSDN/知乎,2019-2024)对同一接口的逆向记录交叉核对,字段名彼此一致,但均非官方文档,developers.weixin.qq.com 未收录此接口。脚本已按此实现并写进代码注释,仍标记为「待用户实测校准」——第一次跑务必 --dry-run --limit 3。
提示:给用户的原始需求里附了一个参考实现链接(
zjp1997720/wechat-article-search);实测那个项目实际调用的是搜狗微信搜索(weixin.sogou.com),并没有用 cookie 抓profileext的技术路线,和"读全部历史"的目标不匹配,所以路 B 没有照抄它,改成上面这套被多方独立复现过的profileext方案。
key/passticket/appmsgtoken 是会话票据,社区报告从几小时到几天不等;没有刷新机制,过期后报错就得重新登录抓包替换。--sleep 1.5 秒,别调太快;批量抓取整年历史建议分批(配合 --limit)跑,别一次性拉几百篇。登录 mp.weixin.qq.com 后台,打开该公众号任意一篇历史文章(或 profile_ext?action=home),在浏览器开发者工具「网络」面板里找同域请求,从请求 URL 里复制 __biz/key/passticket/appmsgtoken,从请求头复制完整 Cookie 字符串。按 assets/config.example.yml 填进私有 config.yml。
python3 scripts/fetch_cookie.py --biz <__biz> [--out <目录>] [--limit N] [--dry-run] \
[--vault <path>] [--account-name <显示名>] [--force] [--sleep 1.5]
canmsgcontinue/next_offset 翻页。contenturl 后,脚本直接 GET 文章公开页面解析 #jscontent(和 soia-pkm-clip-wechat-article 单篇归档同一套正文抓取思路,不需要额外 cookie)。--dry-run 依然会翻完整个列表、抓完全部正文再打印(不写文件),量大时耗时较长——想快速核对列表本身,先看 stderr 里逐页打印的 拿到 N 条,累计 M。soia-pkm-organize-article-moc 归位后的最终位置——organize 上线前先囤在这里,之后随"作品库重构"一起归位到 40/50 区。Inbox/gzh-articles/<年>/(vault 内相对路径,代码里的通用默认值)。这个 vault 建议设为你自己的中转区,例如 <vault-inbox-dir>/gzh-articles/——用 --out <vault-relative-dir> 或私有配置里的 env.OBSIDIANGZHOUT 覆盖(默认值刻意不硬编码具体中文路径,遵守本仓库 [SKILLSPEC.md](../../SKILLSPEC.md) 的"no hardcoded personal paths"规则)。<出版日期>-公众号-<账号显示名>-<标题>.md,同名冲突时按 url 特征加短后缀。tags:[公众号原创, 待归位]、source: 公众号自有、url、title、author、publishedat、capturedat、route: api|cookie|mp-backend、content_complete、topics: [](topics 是本 skill 相对用户原始字段清单多加的一项,方便后续 organize 直接补分类,无内容时留空数组,不影响其他字段)。html.parser(零第三方依赖,和这个仓库其它脚本一致),不保证 100% 还原排版,复杂内联样式/公众号专属组件会被拍平成纯文本+图片链接。content_complete: false 表示正文抓取失败或为空,需要人工核对原文链接。url 在 out_dir 下递归查找是否已归档,已存在则默认跳过(--force 才覆盖),三条路交替跑不会重复落地同一篇。按 clip 家族惯例,AI 后续可以:
topics。organize 从 Inbox/gzh-articles 批量归位。★clip-gzh(批量收) → organize(归位/分类) → distill(收藏→观点) → compose(观点→文) → publish(发)
和 soia-pkm-clip-wechat-article(单篇归档)互补:clip-wechat 处理"看到一篇转发链接,随手存一篇";clip-gzh 处理"批量迁入我自己公众号的历史存量"。两者落地规范不完全一致(clip-gzh 多了 route/content_complete 字段),暂不合并,等作品库重构时一并评估是否收敛成一套。