# wxfetch **Repository Path**: suwu/wxfetch ## Basic Information - **Project Name**: wxfetch - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-28 - **Last Updated**: 2026-11-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # wechat-article-fetcher 把**公开的**微信公众号文章(`mp.weixin.qq.com`)读成干净的 Markdown 或 JSON 的命令行工具 / Python 库。 A small CLI and Python library that turns public WeChat Official Account articles into clean Markdown or JSON. 作者 Yomin Ma · 博客 [yomin.love](https://yomin.love) · GitHub [@mrlong0129](https://github.com/mrlong0129) · MIT License ```bash wxfetch https://mp.weixin.qq.com/s/<文章ID> ``` ## Quick start for agents 零安装、零配置,复制一行就能用(只需要 [uv](https://docs.astral.sh/uv/)): ```bash # 1) 直接读一篇公众号文章(不安装) uvx --from git+https://github.com/mrlong0129/wechat-article-fetcher wxfetch uvx --from git+https://github.com/mrlong0129/wechat-article-fetcher wxfetch -f json # 2) Claude Code:一行注册 MCP server(工具:fetch_wechat_article、parse_wechat_html;需要 Python 3.10+) claude mcp add wechat-article-fetcher -- uvx --from git+https://github.com/mrlong0129/wechat-article-fetcher wxfetch-mcp ``` **Cursor**:写入 `~/.cursor/mcp.json`(全局)或项目里的 `.cursor/mcp.json`: ```json { "mcpServers": { "wechat-article-fetcher": { "command": "uvx", "args": ["--from", "git+https://github.com/mrlong0129/wechat-article-fetcher", "wxfetch-mcp"] } } } ``` **Agent Skill**(不装包也能用,Agent 会照着手册自己读): ```bash # Claude Code mkdir -p ~/.claude/skills/wechat-article-fetcher && curl -fsSL https://raw.githubusercontent.com/mrlong0129/wechat-article-fetcher/main/skill/SKILL.md -o ~/.claude/skills/wechat-article-fetcher/SKILL.md # Cursor mkdir -p ~/.cursor/skills/wechat-article-fetcher && curl -fsSL https://raw.githubusercontent.com/mrlong0129/wechat-article-fetcher/main/skill/SKILL.md -o ~/.cursor/skills/wechat-article-fetcher/SKILL.md ``` 给编码 Agent 的完整说明见 [AGENTS.md](AGENTS.md)。要读微信以外的网页,用姊妹项目 [agent-web-fetch](https://github.com/mrlong0129/agent-web-fetch)。 ## 功能 - 支持短链接 `https://mp.weixin.qq.com/s/`、长链接 `...?__biz=...&mid=...&idx=...&sn=...`、不带 `https://` 的链接、从网页源码里复制出来带 `&` 的链接 - 一次多篇,或者从文件 / 标准输入批量读(会自动从一段文字里挑出公众号链接) - 输出 Markdown(默认)或 JSON:标题、公众号名、作者、发布时间(北京时间 ISO 8601)、封面、摘要、正文 Markdown、纯文本、图片列表、用了哪种提取方式 - 被微信的「环境异常 / 去验证」页面挡住时自动换 User-Agent 重试;文章被删、违规、链接错误时直接说明原因 - 可选 `--download-images`:把图片存到本地并改写 Markdown 里的图片链接 - 依赖很少:`requests` + `beautifulsoup4` + `lxml` ## 安装 需要 Python 3.8+。 ```bash git clone https://github.com/mrlong0129/wechat-article-fetcher.git cd wechat-article-fetcher python3 -m venv .venv && source .venv/bin/activate pip install -e . # 开发 / 跑测试:pip install -e '.[dev]' wxfetch --help ``` ## 用法 ```bash # 一篇文章 → Markdown 输出到终端 wxfetch https://mp.weixin.qq.com/s/<文章ID> # JSON(单篇输出一个对象,多篇输出数组) wxfetch https://mp.weixin.qq.com/s/<文章ID> -f json # 多篇写进一个文件 wxfetch URL1 URL2 -o articles.md # 从文件批量读(每行一个链接,# 开头的行忽略;也可以直接丢一段聊天记录进去) wxfetch -i urls.txt --out-dir out/ # 每篇一个文件,并把图片下载到本地(out/<日期-标题>_images/) wxfetch -i urls.txt --out-dir out/ --download-images # Markdown 顶部加 YAML front matter(方便 Obsidian / 静态博客) wxfetch URL --front-matter -o note.md # 解析浏览器里「另存为」的 HTML,不联网 wxfetch --from-html saved_page.html -f json # 查看抓取过程:用了哪个 UA、有没有被要求验证 wxfetch -v URL ``` | 参数 | 说明 | | --- | --- | | `-f markdown\|json` | 输出格式,默认 markdown | | `-o FILE` / `--out-dir DIR` | 写进一个文件 / 每篇一个文件 | | `-i FILE` | 从文件读链接,`-` 表示标准输入 | | `--from-html FILE` | 解析本地 HTML,可重复 | | `--download-images` / `--images-dir DIR` | 下载图片并改写链接 | | `--front-matter` | Markdown 加 YAML 头 | | `--ua ios\|android\|desktop` | 只用指定 UA(可重复,按顺序尝试) | | `--delay 2.0` | 多篇之间的间隔秒数(带随机抖动) | | `--retries 2` / `--timeout 20` | 每个 UA 的网络重试次数 / 单次超时 | 退出码:`0` 全部成功,`1` 至少一篇失败(错误信息在 stderr),`2` 用法错误。 ### JSON 字段 下面是用测试里的示例页面(占位内容)跑出来的结果: ```jsonc { "url": "https://mp.weixin.qq.com/s/ExampleTextPost01", "title": "这是一条用于测试的示例短内容,正文全部是占位文字。", "account_nickname": "示例公众号", // 公众号名 "author": "示例作者", // 文章作者(可能和公众号名不同) "publish_time": "2026-10-05T23:59:51+08:00", "publish_timestamp": 1791215991, "cover_image": "", "description": "", "content_markdown": "这是一条用于测试的示例短内容……", "content_text": "这是一条用于测试的示例短内容……", "images": [{"url": "https://mmbiz.qpic.cn/...", "alt": "", "local_path": "note_images/001.png"}], "extraction_method": "js_content_noencode", // 哪种提取方式胜出 "extraction_candidates": {"js_content_noencode": 129, "og_meta": 129}, // 各方式提取到的字数 "canonical_url": "https://mp.weixin.qq.com/s/ExampleTextPost01", "account_id": "gh_000000000000", "account_alias": "ExampleAccount", "item_show_type": 10, // 0 普通图文,10 纯文字短内容,8 图片消息 "fetched_with": "ios", // 哪个 UA 拿到的页面 "char_count": 140 } ``` ### 作为 Python 库 ```python from wxfetch import fetch_article, extract_article art = fetch_article("https://mp.weixin.qq.com/s/<文章ID>") print(art.title, art.account_nickname, art.publish_time) print(art.content_markdown) art = extract_article(open("saved.html", encoding="utf-8").read()) # 离线解析 ``` ## How it works:怎么读一篇公众号文章 > 这一节写成了可以单独阅读的形式,不需要先看上面的用法说明。 ### 问题:同一个链接,不同的人看到不同的页面 把一篇公众号文章的链接丢给一个通用的网页抓取服务,经常拿不到正文,只拿到一个写着「环境异常,完成验证后即可继续访问」的页面。可同一个链接在手机微信里点开一切正常。 原因很简单:网站会根据「你是谁、从哪来」决定给你什么。公众号文章本来就是给手机微信的内置浏览器看的;一个从数据中心 IP 发出、自称是某个爬虫的请求,在微信看来就很可疑,于是先丢一个验证页给你。 ### 第一步:像手机微信那样去请求 每个 HTTP 请求都带一行 `User-Agent`,告诉网站「我是什么浏览器」。手机微信的 User-Agent 里有一段 `MicroMessenger/8.0.x`,网站就是靠它认出「这是微信」。 所以第一招就是把请求打扮成微信内置浏览器:iPhone 微信的 User-Agent,加上 `Referer: https://mp.weixin.qq.com/` 和 `Accept-Language: zh-CN`。实测公开文章的短链接用这一招基本都能直接拿到完整页面。拿不到就换 Android 微信,再不行换普通桌面 Chrome。 这不是破解。拿到的是任何人用微信打开这个公开链接都能看到的同一个页面,没有绕过登录、付费或者验证码。 ### 第二步:先判断拿到的是什么页面 请求回来的不一定是文章,所以先分类: - **验证页**:被跳转到 `/mp/wappoc_appmsgcaptcha`,或页面里有 `secitptpage`、「环境异常」「去验证」。这时换下一个 User-Agent,**不去尝试破解验证码**。 - **错误页**:页面里有一个 `weui-msg__title`,写着「该内容已被发布者删除」「此内容因违规无法查看」「参数错误」之类。这种情况换什么方式都没用,直接停下来,把原因告诉用户。 - **网络错误、限流(429)、服务端错误(5xx)**:同一种方式稍等再试,等待时间指数增长并带随机抖动。 批量读的时候每篇之间默认停 2 秒左右,别给对方添麻烦。 ### 第三步:正文其实藏在好几个地方 拿到的页面有两三 MB,大部分是 JavaScript。正文通常不止一份: 1. **`
`**:普通长文的正文就在这里。要用真正的 HTML 解析器去解析,不要用正则「从 js_content 截到 `