Install
openclaw skills install wechat-to-obsidian将微信公众号文章剪藏到 Obsidian:使用真实无头浏览器加载页面,按原文顺序保留文字、图片和 Markdown 标题层级,图片自动下载到 attachments 目录。
openclaw skills install wechat-to-obsidian这个 Skill 用来把 mp.weixin.qq.com 微信公众号文章保存成 Obsidian Markdown 笔记。
核心目标:
agent-browser 打开页面,避免普通 curl / web_fetch 被微信反爬拦截。attachments/ 目录。h1-h6 和微信常见“视觉标题”,输出 ## / ###。当用户发来微信公众号链接,并表达以下意图时使用本 Skill:
链接通常长这样:
https://mp.weixin.qq.com/s/xxxxx
agent-browser ≥ 0.17curl安装 agent-browser:
npm install -g agent-browser
agent-browser install
agent-browser 打开微信公众号文章。<note_dir>/attachments/。agent-browser open "<wechat_url>"
agent-browser wait --load networkidle
如果 networkidle 等待失败,但页面正文已经可见,可以继续执行下一步。
agent-browser get title
优先从页面里的 #activity-name 读取标题;如果失败,再用浏览器标题兜底。
微信图片大量使用懒加载。必须滚动全文后再提取图片 URL,否则图片 src 可能为空。
agent-browser eval "
(async () => {
window.scrollTo(0, document.body.scrollHeight);
await new Promise(r => setTimeout(r, 2000));
const step = 600;
for (let y = 0; y < document.body.scrollHeight; y += step) {
window.scrollTo(0, y);
await new Promise(r => setTimeout(r, 300));
}
return 'done';
})()"
重要规则:
agent-browser eval 内部尽量使用经典 function(){} 语法。JSON.stringify() 包裹的大段 JS 里混用复杂 shell 引号。type:heading / text / img。heading 渲染成 Markdown 标题,而不是普通文本。agent-browser eval "
(function() {
function textOf(node) {
return (node.innerText || '').replace(/\u00a0/g, ' ').trim();
}
function maxFontSize(node) {
var max = 0;
var els = [node].concat(Array.prototype.slice.call(node.querySelectorAll('*')));
els.forEach(function(el) {
var n = parseFloat(getComputedStyle(el).fontSize || '0');
if (n > max) max = n;
});
return max;
}
function hasBold(node) {
if (node.querySelector('strong,b')) return true;
var els = [node].concat(Array.prototype.slice.call(node.querySelectorAll('*')));
return els.some(function(el) {
var fw = getComputedStyle(el).fontWeight || '400';
return fw === 'bold' || parseInt(fw, 10) >= 600;
});
}
function headingLevel(node, text) {
var tag = node.tagName;
if (/^H[1-6]$/.test(tag)) return parseInt(tag.slice(1), 10);
var size = maxFontSize(node);
var bold = hasBold(node);
var short = text.length <= 40;
// 微信常把视觉标题做成带样式的 p / section,而不是 h2 / h3。
// 这里保守识别常见中文大纲标题和数字小标题。
if (/^[一二三四五六七八九十]+[\..、]\s*/.test(text) && (bold || size >= 19 || short)) return 2;
if (/^\d+[\..、]\s*/.test(text) && (bold || size >= 19 || short)) return 3;
if (short && bold && size >= 20) return 3;
return 0;
}
function inlineMarkdown(node) {
var text = textOf(node);
if (!text) return '';
if (node.querySelector('code')) return text.replace(/`/g, '\\`');
return text;
}
var nodes = document.querySelectorAll('#js_content h1,#js_content h2,#js_content h3,#js_content h4,#js_content h5,#js_content h6,#js_content p,#js_content section,#js_content img');
var result = [];
var imgIdx = 0;
nodes.forEach(function(node) {
if (node.tagName === 'IMG') {
var src = node.currentSrc || node.src || node.dataset.src || '';
if (src && src.includes('mmbiz') &&
!src.includes('mmbiz.qlogo') &&
!src.includes('profile')) {
var h = node.naturalHeight || node.height || 0;
var alt = (node.alt || '').toLowerCase();
if ((h >= 50 || h === 0) &&
!alt.includes('二维码') &&
!alt.includes('引导') &&
!alt.includes('赞赏')) {
result.push({ type: 'img', idx: imgIdx++, src: src });
}
}
} else {
if (node.tagName === 'SECTION' && node.querySelector('p,img,section')) return;
var text = textOf(node);
if (text && text.length > 3) {
var level = headingLevel(node, text);
if (level) result.push({ type: 'heading', level: level, text: text });
else result.push({ type: 'text', text: inlineMarkdown(node) });
}
}
});
return JSON.stringify(result);
})()"
h1-h6 → # 到 ######一. / 一、 / 二. 这类章节标题 → ##1. Mac / 2. Windows 这类数字小标题 → ######写入时示例:
## 一. Claude Code 安装
### 1. Mac
图片仍然必须保持在 DOM 原始位置,不要统一堆到文末。
跳过这些图片:
mmbiz.qlogo:公众号头像mp_profile:账号资料图alt 包含 二维码 / 引导 / 赞赏:二维码、引导关注、赞赏图如果用户已经明确给出保存路径,可以直接进入 Step 6。
如果用户没有明确路径,必须停下来询问:
📂 建议保存到:<vault_root>/<topic_directory>/
📄 文件名:<title-keywords-YYYY-MM-DD>.md
🖼 图片目录:同级 attachments/
确认保存到这里吗?还是换个位置?
HARD STOP:用户确认前,不要下载图片,不要写入笔记。
只在用户确认保存路径后执行。
mkdir -p "<note_dir>/attachments"
curl -s -L --fail \
-A 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' \
-e 'https://mp.weixin.qq.com/' \
"<image_url>" -o "<note_dir>/attachments/<filename>"
关键点:
-e 'https://mp.weixin.qq.com/'<slug>-图00.png、<slug>-图01.jpgwx_fmt=jpeg,扩展名用 .jpg;否则默认 .pngdeclare -A,因为 zsh 不支持关联数组批量下载函数示例:
download_img() {
local idx=$1 url=$2
local ext="png"
echo "$url" | grep -q "wx_fmt=jpeg" && ext="jpg"
local fname=$(printf "<slug>-图%02d.%s" "$idx" "$ext")
curl -s -L --fail \
-A 'Mozilla/5.0' \
-e 'https://mp.weixin.qq.com/' \
"$url" -o "$fname" \
&& echo "OK $fname" || echo "FAIL $fname"
}
必须严格使用 Step 4 的 DOM 顺序。
# {文章标题}
**Source:** WeChat — {作者 / 公众号名}
**Original URL:** {URL}
**Clipped:** {YYYY-MM-DD}
**Tags:** #{tag1} #{tag2}
---
{正文段落}
![[slug-图00.jpg]]
## {二级标题}
{更多正文}
![[slug-图01.png]]
---
**References:**
- {原文链接或正文链接}
注意:
{ type: 'heading', level: 2, text: '...' } 必须写成 ## ...{ type: 'heading', level: 3, text: '...' } 必须写成 ### ...![[filename]]attachments/ 目录agent-browser close
向用户说明:
attachments/ 一起移动| 问题 | 修复 |
|---|---|
图片 src 为空 | 先滚动全文,触发懒加载 |
| 微信图片下载 403 | curl 必须带 -e 'https://mp.weixin.qq.com/' |
| 图片顺序错乱 | 按 DOM 单次遍历,不要文字和图片分开收集 |
zsh 报 bad substitution | 不要用 declare -A;改用 shell function |
SyntaxError: missing ) after argument list | agent-browser eval 里少用复杂箭头函数和嵌套引号 |
async eval 卡住 | 用 (async () => { ... })() 包住 |
| 标题变成普通文字 | 使用 heading 类型并在写入时转成 ## / ### |