v1.3.1 to v1.4.0

60 added, 207 removed. Audit A to A.

---
name: wechat-article-fetch
homepage: https://github.com/cat-xierluo/legal-skills
author: 杨卫薪律师(微信ywxlaw)
- version: "1.3.1"
- description: 抓取微信公众号文章内容,使用 Playwright headless 模式无弹窗后台抓取,支持动态加载内容,自动提取标题和正文并保存为 Markdown 文件。本技能应在用户需要抓取微信公众号文章内容时使用。检测到法律内容且项目中存在 legal-text-format 时自动链式调用格式化。默认保存到 skill 内部 archive 目录。
+ version: "1.4.0"
+ description: 抓取微信公众号文章(mp.weixin.qq.com)到本地。当用户发来公众号链接要求"抓取""保存""存下来""归档""留痕""转成 Markdown"时使用。默认双产出:Markdown 整理版(供检索引用)+ HTML 原件保真归档(离线双击可读,供回溯核对)。默认 HTTP 直抓零依赖,失败自动切浏览器兜底。
license: Complete terms in LICENSE.txt
---
# 微信公众号文章抓取工具
## 概述
- 使用 Playwright 抓取微信公众号文章,后台运行无弹窗,自动处理动态加载,提取干净的文章内容,并支持自动保存为 Markdown 文件。
-
- ## 功能特性
-
- - ✅ **无头模式运行**: 默认后台抓取,不弹出浏览器窗口
- - ✅ **智能回退机制**: 无头模式失败时自动切换到有头模式
- - ✅ **动态内容支持**: 自动等待页面加载完成,处理懒加载图片
- - ✅ **自动保存为 Markdown**: 支持将抓取结果保存为格式化的 Markdown 文件
- - ✅ **内容清洗**: 移除HTML标签,保留段落结构,输出纯文本
- - ✅ **自动重试**: 失败时自动重试3次,提高成功率
- - ✅ **错误检测**: 识别"参数错误"等异常页面
- - ✅ **跨平台支持**: 完全支持 Windows、macOS 和 Linux
- - ✅ **法律内容自动格式化**: 检测到法律内容时自动链式调用 legal-text-format(强制执行协议)
- - ✅ **图片下载**: 自动下载文章中的所有图片到本地
- - ✅ **智能图片筛选**: 自动过滤小的装饰性图片(如社交媒体按钮、表情符号)
- - ✅ **图片位置保持**: 保留图片在原文档中的位置
- - ✅ **自动文件命名**: 根据文章标题生成文件名和资源文件夹
-
- ## 抓取后法律格式化协议(强制执行)
-
- **本节为必须遵守的执行指令,而非可选建议。**
-
- ### 法律内容判定清单
-
- 完成抓取后,你必须立即按以下清单判定内容是否为法律文本。**满足任意一条即为法律内容**:
-
- - **标题关键词**:案例、裁判、判决、法规、条例、规定、办法、解释、通知、意见、规则、最高法、最高检、法院、检察院、司法解释
- - **内容特征**:包含案号(如 `(202X)XX民初XX号`)、法院名称、法律条文引用(如 `第X条第X款`)
- - **结构特征**:符合法律案例典型结构(基本案情、裁判结果、典型意义等)
-
- ### 自动链式调用条件
-
- **以下两个条件同时满足时,必须立即调用 `legal-text-format`,不询问用户确认**:
-
- 1. 上方判定结果为"是法律内容"
- 2. 本项目中存在 `skills/legal-text-format/SKILL.md`(通过 `ls skills/legal-text-format/SKILL.md` 或等效方式检查)
-
- ### 链式调用步骤
-
- 条件满足时,按以下顺序执行:
-
- 1. 完成抓取,将 Markdown 保存到本技能 `archive/` 目录
- 2. 按判定清单判断内容类型
- 3. 如果是法律内容,检查 `skills/legal-text-format/SKILL.md` 是否存在
- 4. **如果存在,立即调用 `legal-text-format` 技能**,传入抓取到的文件路径作为输入
- 5. 使用下方对应模板汇报最终结果
-
- ### 降级处理
-
- 如果 `legal-text-format` 不存在:正常结束抓取流程,在汇报中附加提示:
-
- > 已检测到法律内容,但项目中未安装 `legal-text-format` 格式化技能。如需自动格式化,可通过 skill-manager 安装。
-
- ### 结果存储
+ 抓取微信公众号文章到本地,默认一次产出两份:
- - **抓取的原始内容**:保存在本技能的 `archive/` 目录(默认行为不变)
- - **格式化后的内容**:保存在 `legal-text-format` 的 `archive/` 目录(由该技能自行管理)
+ | 产物 | 用途 | 特点 |
+ |---|---|---|
+ | Markdown 整理版 | 检索、引用、下游加工 | 纯文本段落 + 图片本地化,自动过滤小装饰图 |
+ | HTML 原件 | 保真留痕、回溯核对 | 完整页面 + 全部图片本地化(不过滤),离线双击可读,原外链保留在 `data-src` 备查 |
- ### 汇报模板
+ 为什么两份都要:公众号分享链接常带 `tempkey` 临时凭证,过期后打不开;Markdown 整理会丢排版和配图,日后无法回溯核对原文。**原件保真,整理版好用,两者互补不互替。**
- **仅抓取**(非法律内容,或 legal-text-format 未安装):
- > 已完成抓取:{标题}
- > 保存位置:{文件路径}
+ 抓取引擎:
- **抓取 + 格式化**(法律内容且 legal-text-format 已安装):
- > 已完成抓取 + 法律格式化:{标题}
- > 原始内容:{wechat-article-fetch archive 路径}
- > 格式化内容:{legal-text-format archive 路径}
- > 文本类型:{法律条文/法律案例}
+ - **HTTP 直抓**(默认主路线):微信文章是服务端渲染,正文与图片地址都在初始 HTML 里,直接请求即可。零依赖、速度快。
+ - **Playwright 浏览器兜底**(`--engine auto` 自动切换):HTTP 路线失败或页面需要 JS 渲染时启用。
## 使用方法
- ### 在 Claude Code 中调用
-
- ```javascript
- // 抓取文章(仅返回结果)
- const result = await fetchWechatArticle("https://mp.weixin.qq.com/s/xxxxx");
-
- // 抓取文章并自动保存为 Markdown 文件
- const result = await fetchWechatArticle(
- "https://mp.weixin.qq.com/s/xxxxx",
- 3, // 重试次数(可选)
- "./output.md" // 保存路径(可选)
- );
-
- // 返回格式
- {
- title: "文章标题",
- content: "文章正文...",
- url: "文章URL"
- }
- ```
-
- ### 命令行调用
-
```bash
- # 基本用法(仅输出到控制台)
+ # 默认:双产出,保存到 skill 内 archive/<时间戳>_<标题>/
node scripts/fetch.js "https://mp.weixin.qq.com/s/xxxxx"
- # 保存为指定文件
- node scripts/fetch.js "https://mp.weixin.qq.com/s/xxxxx" "./articles/my-article.md"
-
- # 保存到目录(自动使用文章标题作为文件名)
+ # 指定输出根目录(文章产物归入其下子目录)
node scripts/fetch.js "https://mp.weixin.qq.com/s/xxxxx" "./articles/"
- ```
- ## 输出格式
-
- ### 控制台输出
-
- ```text
- 标题: 文章标题
-
- 文章正文第一段...
-
- 文章正文第二段...
- ```
-
- ### Markdown 文件格式
-
- ```markdown
- # 文章标题
-
- > 原文链接: https://mp.weixin.qq.com/s/xxxxx
- > 抓取时间: 2026-01-21 20:30:00
-
- ---
-
- 文章正文第一段...
+ # 兼容旧用法:直接指定 .md 文件路径(其余产物同目录平铺)
+ node scripts/fetch.js "https://mp.weixin.qq.com/s/xxxxx" "./articles/my-article.md"
- ![图片描述](文章标题_assets/image_xxx_0.jpg)
+ # 只要 Markdown 整理版 / 只要 HTML 原件
+ node scripts/fetch.js "https://mp.weixin.qq.com/s/xxxxx" --mode markdown
+ node scripts/fetch.js "https://mp.weixin.qq.com/s/xxxxx" --mode archive
- 文章正文第二段...
+ # 强制走浏览器(或只走 HTTP 不装 Playwright)
+ node scripts/fetch.js "https://mp.weixin.qq.com/s/xxxxx" --engine playwright
+ node scripts/fetch.js "https://mp.weixin.qq.com/s/xxxxx" --engine http
```
- ### 文件结构
+ 完整参数运行 `node scripts/fetch.js --help` 查看。
- 当文章包含图片时,会自动生成以下文件结构:
+ ### 产物结构
```
- 输出目录/
- ├── 文章标题.md # Markdown 文件
- └── 文章标题_assets/ # 图片资源文件夹
- ├── image_xxx_0.jpg
- ├── image_xxx_1.jpg
- └── ...
+ archive/<YYYYMMDD_HHMMSS>_<文章标题>/
+ ├── <文章标题>.md # 整理版(头部注明原文链接与 HTML 原件位置)
+ ├── <文章标题>.html # 原件(src 已本地化,离线可读)
+ ├── <文章标题>_assets/ # 整理版图片(智能过滤小装饰图)
+ └── <文章标题>_html_assets/ # 原件图片(全量保真,含小图标)
```
- ### 图片筛选
-
- 默认启用智能图片筛选,自动过滤小于 15KB 的装饰性图片(如社交媒体按钮、表情符号等)。
-
- 可以在 `scripts/fetch.js` 中修改筛选配置:
+ ### 编程接口
```javascript
- const IMAGE_FILTER_CONFIG = {
- minFileSize: 15 * 1024, // 最小文件大小(字节)
- enabled: true // 是否启用筛选
- };
- ```
-
- ## 技术实现
-
- ### 依赖要求
-
- - Playwright (`npx playwright install chromium`)
- - Node.js >= 14.0.0
-
- ### 抓取流程
-
- 1. 检测并安装 Playwright(如需要)
- 2. 启动 Playwright headless 浏览器
- 3. 设置反检测参数(User-Agent, webdriver隐藏等)
- 4. 导航到目标URL,等待网络空闲
- 5. 滚动页面触发懒加载
- 6. 提取 `#js_content`或 `.rich_media_content`区域
- 7. 清理HTML标签,保留段落结构
- 8. 返回标题和纯文本内容
- 9. **如果指定了保存路径,自动保存为 Markdown 文件**
- 10. **如果无头模式失败,自动回退到有头模式重试**
-
- ### 错误处理
-
- - 自动重试3次,每次失败后等待3秒
- - **无头模式失败后自动回退到有头模式**
- - 检测错误页面(参数错误、访问异常)
- - 超时设置30秒
- - **Windows 平台特殊处理(路径、命令格式)**
-
- ### 跨平台兼容性
-
- - **Windows**: 自动检测并使用 `cmd.exe` 运行 npx 命令
- - **macOS/Linux**: 直接使用 npx 命令
- - **路径处理**: 自动规范化路径分隔符
- - **文件名处理**: 自动移除 Windows 非法字符
+ import { fetchWechatArticle } from './scripts/fetch.js';
- ## 适用场景
+ const report = await fetchWechatArticle("https://mp.weixin.qq.com/s/xxxxx", {
+ engine: 'auto', // 'auto' | 'http' | 'playwright'
+ mode: 'both', // 'both' | 'markdown' | 'archive'
+ outputPath: null, // 输出路径(可选)
+ retries: 3 // Playwright 路线重试次数
+ });
+ // report: { title, url, engine, md, archive: { htmlPath, images, missing, remoteLeft, failed } }
+ ```
- - 内容转换工具的输入源
- - 文章分析和处理
- - 自动化内容抓取
- - 批量文章下载
- - **文章归档和本地保存**
- - **Markdown 格式转换**
- - **法律文档自动格式化**(检测到法律内容时)
- - **图文文章完整保存**(包含图片的离线归档)
- - **图片资源管理**(自动下载并组织文章中的图片)
+ ## 抓取后检查(必做,不要跳过)
- ## 使用示例
+ 看脚本输出:
- ### 示例 1: 批量抓取并保存
+ - HTML 原件行首为 `[OK ]` 即归档完整;`[警告]` 时查看"缺失本地图"与"残留外链"两项
+ - 残留外链只剩微信官方空白占位 `pic_blank.gif` 属正常,可忽略
+ - Markdown 的图片统计里"失败"应为 0,过滤数是装饰图属正常
+ - 页面报"该内容已被发布者删除""环境异常"等 → 链接失效,脚本会自动切换引擎重试;两条引擎都失败时不要再盲试链接
- ```javascript
- const urls = [
- "https://mp.weixin.qq.com/s/xxxx1",
- "https://mp.weixin.qq.com/s/xxxx2",
- "https://mp.weixin.qq.com/s/xxxx3"
- ];
+ ## 陷阱与要点
- for (const url of urls) {
- const result = await fetchWechatArticle(url, 3, "./articles/");
- console.log(`已保存: ${result.title}`);
- }
- ```
+ | 坑 | 处理 |
+ |---|---|
+ | `tempkey` 链接会过期 | **收到就立刻抓**,不要等"有空再存" |
+ | 图片真地址在 `data-src` 不在 `src` | `src` 多为 base64 占位;脚本已处理,手工排查时注意 |
+ | URL 属性里是 `&amp;` 不是 `&` | 解码后才能下载,否则 403;脚本已处理 |
+ | 图片域名不止 `mmbiz.qpic.cn` | 还有 `mmecoa.qpic.cn` 等,按 `qpic.cn` 后缀识别,别写死子域 |
+ | `wx_fmt` 参数定扩展名 | 微信图 URL 无 `.jpg` 后缀,靠 `?wx_fmt=jpeg` 推断,PNG 不会存成 jpg 名 |
+ | svg 小图标看着像 0KB | 原件目录里的几百字节装饰图标不是下载失败,别删(原件全量保真) |
+ | "环境异常"页 | 引擎自动切换重试;仍失败说明链接失效或需验证 |
- ### 示例 2: 在 Claude Code 中直接使用
+ ## 下游加工建议
- ```text
- 请帮我抓取这个微信公众号文章并保存为 Markdown 文件:
- https://mp.weixin.qq.com/s/xxxxx
- ```
+ 抓取完成后,如内容为法律法规、裁判文书等结构化法律文本,可在汇报时**提示用户**:如需进一步格式化整理,可指定相应的格式化技能(若环境中有)继续处理。是否调用、调用哪个技能由用户或上层流程决定——本技能不自动调用、不绑定任何下游技能。
## 注意事项
- ⚠️ 仅用于个人学习和研究,请遵守网站服务条款
- ⚠️ 频繁抓取可能被限流,建议控制请求频率
- ⚠️ 抓取的内容版权归原作者所有
- ⚠️ **有头模式会弹出浏览器窗口,可能干扰工作流程**
- ⚠️ **Windows 用户首次使用需要安装 Playwright(会自动安装)**
+ ⚠️ 仅用于个人学习和研究,请遵守网站服务条款
+ ⚠️ 频繁抓取可能被限流,建议控制请求频率
+ ⚠️ 抓取内容版权归原作者所有
+ ⚠️ Playwright 兜底路线首次使用会自动安装 chromium(`npx playwright install chromium`);HTTP 直抓路线零依赖