Fetch Archive to Lexiang — Fetch 归档 to Lexiang
v0.1.2通用文章抓取与归档工具。抓取任意 URL(免费/付费/登录墙)的文章全文,转换为结构化 Markdown,并可选转存到乐享知识库。支持 Substack、Medium、知识星球等付费平台的登录态管理。支持 YouTube 视频下载(yt-dlp)、播客音频下载(小宇宙FM等)、音频转录(Whisper)、翻译(中英对照格式),并将音视频和文字稿上传乐享知识库(文字稿使用在线文档格式,支持按块编辑)。关键词触发:抓取文章、获取全文、付费文章、转存知识库、乐享、保存原文、fetch article、归档、YouTube、视频转录、字幕提取、视频下载、播客、podcast、小宇宙、xiaoyuzhou。
运行时依赖
安装命令
点击复制技能文档
抓取链接内容 & 转存知识库 概述
将文章 URL(免费/付费/登录墙)抓取为结构化 Markdown,并自动转存到乐享知识库,实现素材归档和可追溯。
最终产出物 <项目子目录>/<原文标题>.md — 完整文章 Markdown(含图片引用) <项目子目录>/<原文标题>_meta.json — 结构化元信息(原文链接、作者、发布时间、抓取时间等) <项目子目录>/images/ — 所有文章配图 乐享知识库中的文档副本(按天维度归档) 文件命名规则(重要) 必须使用原文标题命名,不要用 article.md 等通用名称 文件名格式:<原文标题>.md、<原文标题>_meta.json 示例:How Notion uses Custom 代理s.md、How Notion uses Custom 代理s_meta.json 如果标题中包含文件名不合法字符(/、\、:等),替换为 - 乐享知识库转存时也使用原文标题作为文档标题 工作流程 Step 1:素材收集 抓取方式决策树
根据 URL 类型选择抓取方式(按优先级排列):
微信公众号文章(mp.weixin.qq.com)→ 必须用 fetch_article.py(网页_fetch 无法获取图片) YouTube 视频 → 使用 yt_下载_transcribe.py(yt-dlp 下载 + Whisper 转录 + AI 翻译),详见下方「YouTube 视频处理」章节 播客音频(小宇宙 xiaoyuzhoufm.com、应用le Podcasts 等)→ yt-dlp 下载音频 + Whisper 转录,详见下方「播客音频处理」章节 付费/登录墙文章 → 用 fetch_article.py(Cookie 注入或 CDP 模式) 免费图文文章(正文含图片/截图/图表)→ 必须用 fetch_article.py(网页_fetch 只能返回文本,无法提取和下载页面中的图片) 免费纯文字文章(正文无配图)→ 可用 网页_fetch,内容不完整时切换 fetch_article.py 文字观点 → 直接整理 图片素材 → 分析图片内容
⚠️ 关键原则:网页_fetch 工具只能返回文本内容,无法提取和下载页面中的图片。任何包含图片、截图、图表的文章,都必须使用 fetch_article.py 抓取,否则图片信息会完全丢失。当不确定文章是否含图时,默认用 fetch_article.py。
付费/登录墙文章获取
适用于所有需要登录态才能查看全文的网站(Substack 付费订阅、Medium 会员、知识星球、财新网、The In格式化ion 等),使用 fetch_article.py 脚本:
# Cookie 注入模式(默认,适用于大部分站点) python scripts/fetch_article.py fetch --输出-dir <项目子目录>
# CDP 模式(适用于 Cloudflare 保护站点、需要 Google 账号登录的站点) python scripts/fetch_article.py fetch --输出-dir <项目子目录> --cdp
两种浏览器模式:
模式 参数 原理 适用场景 Cookie 注入 (默认) 从 Chrome Cookie DB 提取 cookies → 注入 Playwright 浏览器 Substack、Medium 等大部分站点 CDP --cdp 通过 Chrome Dev工具s Protocol 连接用户真实 Chrome(port 9222),复用完整登录态 OpenAI、Cloudflare 保护站点、LinkedIn、Google 系网站等会检测自动化浏览器或有反爬的站点
何时必须用 CDP 模式:
Cloudflare 保护站点(如 openAI.com):Cloudflare 的 JS challenge 会阻拦 Playwright 自动化浏览器。CDP 模式连接用户真实 Chrome 可以绕过。脚本会自动检测 Cloudflare challenge 页面("Just a moment..." 等)并等待验证通过。 Google 登录页面(如 LinkedIn "通过 Google 登录"):Google 登录页面会检测 Playwright 并拒绝登录。CDP 模式完全绕过此检测。
自动升级:对于已知的 Cloudflare 站点(openAI.com 等),即使未指定 --cdp,脚本也会自动切换到 CDP 模式。
CDP 模式前置条件:确保 Chrome 浏览器已开启 CDP 远程调试端口:
# 启动 Chrome(开启 CDP 远程调试端口 9222) /应用s/Google\ Chrome.应用/Contents/MacOS/Google\ Chrome --remote-调试ging-port=9222 &
# 验证 CDP 端口可连接 curl -s http://localhost:9222/json/version
如果 Chrome 已在运行但未开启 CDP,脚本会尝试优雅关闭 Chrome 后以 CDP 模式重启。
工作原理:
自动从 Chrome 浏览器的 Cookie 数据库提取目标域名的登录 cookies 将 cookies 注入 Playwright 浏览器上下文 加载页面,自动检测并等待 Cloudflare challenge 通过(如有) 滚动加载懒加载内容、下载所有图片 自动格式转换:检测下载图片的真实格式(网页P/SVG 伪装成 .png/.jpg 很常见),自动转为真正的 PNG 以确保 PDF 生成和文档嵌入兼容 将正文转换为 Markdown(article.md),图片保存到 images/ 子目录 内容提取时自动选择最长的内容容器(避免只抓到免费预览区域)
标题提取增强(多策略回退):
CSS 选择器优先级:h1.post-title > article h1 > [class="title"] h1 > h1 回退到 → → document.title 自动清理标题中的网站后缀(如 " - Cursor"、" | Substack") 正文中与已提取标题相同的第一个
会被自动去重,避免 MD 中标题重复作者提取增强:
CSS 选择器 + meta[name="author"] + [rel="author"] + meta[property="article:author"] 多策略回退
微信公众号文章(mp.weixin.qq.com)专项优化: 脚本对微信公众号文章有专门的检测和处理策略:
自动检测:识别 mp.weixin.qq.com 域名,自动启用微信模式
无需登录:微信公众号文章是公开可读的,跳过登录检测和 Cookie 注入流程
专用内容选择器:使用 #js_content / .rich_media_content 精准定位正文区域(而非通用选择器可能匹配到页面其他内容)
标题提取:#activity-name > h1.rich_media_title > 通用 h1 > meta 标签回退
作者提取:#js_name(公众号名称)> .rich_media_meta_nickname > 通用选择器回退
日期提取:#publish_time > 通用 time/date 选择器回退
图片懒加载增强:
微信图片使用 data-src + IntersectionObserver 懒加载
滚动速度放慢(300px 步长、200ms 间隔)以确保触发所有 IntersectionObserver
强制将未触发的 data-src 复制到 src(兜底策略)
图片下载时优先使用 data-src 的高清原图 URL
图片格式识别:微信图片 URL 格式特殊(mmbiz.qpic.cn/...?wx_fmt=png),从 wx_fmt 查询参数推断文件扩展名
Referer 防盗链:通过 Playwright 页面上下文的 page.请求.获取() 下载图片,自动携带正确的 Referer 头
Substack 站点(如 www.lennysnewsletter.com)专项优化: 脚本对 Substack 托管的站点(.substack.com、lennysnewsletter.com 等)有专门的登录检测和登录态缓存**机制:
登录态缓存:登录成功后自动保存 Playwright storage_状态 到 ~/.substack/storage_状态.json,后续抓取直接复用,无需重复登录和邮箱验证 优先级:缓存 storage_状态 > Chrome cookies > 引导登录 自动检测登录状态:加载页面后检查右上角是否有用户头像(已登录)还是 "签名 in" 按钮(未登录) 已登录 → 直接抓取全文,并刷新缓存延长有效期 缓存过期 → 自动清理旧缓存,进入引导登录流程 未登录 → 打开可见浏览器窗口引导登录,用户在终端输入 y 确认后二次验证,通过后自动缓存
独立登录命令(推荐首次使用时先执行):
python scripts/fetch_article.py 记录in
此命令单独完成 Substack 登录并缓存,不需要指定文章 URL。后续所有 Substack 文章抓取都会自动复用此登录态。
非 Substack 站点的登录确认机制:
无 Chrome cookies 时自动切换到非无头模式,打开可见浏览器窗口 终端提示用户完成登录操作后按回车键继续 收到确认信号后重新加载页面并检测付费墙状态
付费墙检测:脚本同时检测以下信号:
DOM 元素:[data-testid="paywall"]、.paywall 文本关键词:This post is for pAId subscribers、Subscribe to read、升级 to pAId 等 注意:不同网站的付费墙 DOM 结构和关键词不同,如遇新网站抓取不完整,需检查页面实际的付费墙标识并更新检测逻辑
判断内容是否完整的方法:
先用 网页_fetch 尝试获取,如果明显被截断(内容不完整、出现付费提示),则切换到 fetch_article.py 抓取完成后必须告知用户查看 article.md 确认内容完整性 关注文章末尾是否有作者署名/总结段落作为完整性标志 如果用户反馈内容不完整,检查:(1) 登录账号是否有付费权限 (2) 页面是否有懒加载内容未触发 (3) 内容选择器是否匹配到了免费预览区而非全文区
产出物:
<项目子目录>/<原文标题>.md — 完整文章 Markdown(含图片引用) <项目子目录>/<原文标题>_meta.json — 结构化元信息(原文链接、作者、发布时间、抓取时间等) <项目子目录>/images/ — 所有文章配图
<原文标题>_meta.json 格式:
{ "url": "原文链接", "title": "文章标题", "subtitle": "副标题", "author": "作者", "date": "发布时间", "content_length": 12345, "image_count": 5, "images": ["images/img_01_xxx.png", ...], "fetched_at": "2026-02-25T10:30:00" }
X.com / Twitter 帖子抓取(必须