[控场AI]
· 8 分钟阅读· 4,146 字

Substack通讯转RAG知识库:干净Markdown管道实战

Substack通讯转RAG知识库:干净Markdown管道实战

用 Substack API 加 turndown 清洗与标题感知分块,构建干净的 RAG 知识管道。

文章介绍了一套把 Substack 通讯转化为高质量 RAG 知识库的完整管道。Substack 作者多为领域深度实践者,内容结构完整,但页面 HTML 充斥订阅按钮、分享组件和图片包装器,直接嵌入会污染检索。管道分五步:先通过归档 JSON 接口按 offset 分页拉取文章元数据;再用单篇接口获取 body_html,并依据 audience 字段尊重付费墙,付费文章只入库元数据;接着用 turndown 转换 Markdown,通过 addRule 丢弃垃圾节点、解开图片链接包装;然后按标题感知方式分块,约 2000 字符上限、200 字符重叠,并保留 url、title、日期、标题等元数据;最后利用归档最新优先特性做增量更新。文章还给出开源代码与 Apify 无代码工具两个现成入口。

Substack 上的通讯(Newsletter)是互联网上质量最高的专家内容之一,非常适合作为 RAG 聊天机器人或 AI 研究代理的知识源。但直接把 Substack 页面丢给嵌入模型会出问题:HTML 里塞满了“Subscribe now”按钮、分享组件和图片包装器,这些噪音会污染向量检索结果。下面是一套从归档列表到分块入库的干净管道,附带踩坑经验。

为什么 Substack 是优质的 RAG 语料

Substack 的作者通常是领域内的深度实践者,文章结构完整、观点明确,且更新频率稳定。相比论坛碎片讨论,通讯更适合作为检索增强生成的知识底座。难点在于提取:Substack 的正文被大量 UI 元素包裹,如果直接用通用 HTML 转 Markdown 工具,会得到一堆无意义的按钮文本和嵌套链接。

这套管道的核心思路是:先通过 API 拿到结构化元数据,再单独抓取正文,然后用 turndown 做定向清洗,最后按语义分块并保留引用元数据。

RAG(检索增强生成) 是一种将外部知识库与大型语言模型结合的架构:用户提问时,系统先从语料库中检索出最相关的文本片段(chunk),再把这些片段作为上下文喂给模型生成答案。它的价值在于弥补模型参数化知识的时效性与领域深度不足,并让回答可溯源。RAG 的效果高度依赖语料质量——如果知识源里混入导航栏、订阅按钮等 UI 噪音,嵌入向量会被这些无意义文本稀释,检索时可能召回一堆“Subscribe now”片段,直接污染生成结果。因此“清洗—分块—入库”这条管道决定了 RAG 系统的上限,这也是本文强调用 API 拿结构化正文、而非直接抓取渲染后 HTML 的根本原因。

第一步:分页拉取文章列表

每个 Substack 出版物——包括像 lennysnewsletter.com 这样的自定义域名——都提供归档 JSON 接口:

curl "https://www.lennysnewsletter.com/api/v1/archive?sort=new&offset=0&limit=50"

通过递增 offset 翻页,直到返回空数组 [] 为止。每个条目包含 title、slug、post_date、audience、wordcount、reaction_count、publishedBylines、postTags 等字段,但不包含正文。这一步只用于建立文章索引和筛选范围。

第二步:抓取正文并尊重付费墙

拿到 slug 后,请求单篇文章接口:

curl "https://www.lennysnewsletter.com/api/v1/posts/{slug}"

响应中的 body_html 就是正文 HTML。这里有一个关键字段 audience:值为 everyone 表示免费文章;付费文章只返回预览内容。原文作者明确建议尊重这一限制——付费文章仅使用元数据入库,不要尝试绕过。

body_html 的质量决定了后续清洗的复杂度。Substack 的正文里嵌入了订阅组件、按钮包装器和图标容器,这些都需要在转换阶段剔除。

付费墙(paywall) 是内容平台按订阅状态限制正文访问的机制。Substack 在单篇接口中用 audience 字段区分可见范围:everyone 为公开文章,其余值(如 only_paid、founding)表示仅向付费订阅者开放,接口只返回预览片段而非完整 body_html。对 RAG 管道而言,这意味着不能假设每篇文章都能拿到全文;工程上应把 audience 作为元数据一并入库,付费文章只保留标题、摘要、日期等索引信息,检索时标记为“仅元数据”。绕过付费墙不仅涉及版权与平台条款风险,也会让知识库的合法性存疑,因此“只取元数据”是既合规又稳健的默认策略。

第三步:用 turndown 转成干净 Markdown

HTML 转 Markdown 用 turndown 是成熟方案,但直接跑会留下垃圾。原文作者总结了两个实战教训:

教训一:用 addRule 丢弃垃圾节点,不要用 remove()。 turndown 内置的段落规则会先匹配到 <p class="button-wrapper">,导致你注册的 remove() 根本不会触发。正确做法是添加自定义规则,在过滤器里按节点名和 class 正则匹配,然后返回空字符串:

td.addRule('junk', {
  filter: (n) => ['FORM', 'BUTTON', 'SVG'].includes(n.nodeName.toUpperCase())
    || /subscription-widget|button-wrapper|captioned-button|icon-container/.test(n.getAttribute?.('class') || ''),
  replacement: () => '',
});

教训二:解开图片链接包装。 Substack 的每张图片都被 <a class="image-link"> 包裹,转换后会变成难看的 [![](img)](img) 嵌套形式。用一个规则把链接层剥掉,只保留图片本身:

td.addRule('imageLink', {
  filter: (n) => n.nodeName === 'A' && /image-link/.test(n.getAttribute('class') || ''),
  replacement: (content) => `\n\n${content.trim()}\n\n`,
});

这两条规则加上默认转换,基本就能得到语义干净的 Markdown 正文。

turndown 是一个把 HTML 转换为 Markdown 的 JavaScript 库,内部由一组“规则(rule)”驱动:每条规则包含 filter(判断某个 DOM 节点是否适用)和 replacement(返回替换后的 Markdown 字符串)。转换时 turndown 按规则注册顺序与节点类型逐一匹配,内置规则已覆盖段落、标题、链接、图片等常见元素。addRule 用于追加自定义规则,可以按节点名、class、属性精确命中目标;而 remove() 则是直接删除匹配节点,但它依赖内置规则不会抢先处理该节点——一旦内置段落规则先命中 <p class="button-wrapper">,节点在到达你的删除逻辑前就已被转换,导致 remove() 形同虚设。理解规则匹配的先后顺序,是写出有效清洗规则的关键。

第四步:按标题感知的方式分块

分块策略直接影响检索质量。原文建议:按空行切分段落,把最近的 # 标题带入每个块,单块上限约 2,000 字符,块间重叠约 200 字符。这种标题感知的分块能让每个块携带上下文归属,避免检索到孤立片段。

元数据同样重要:为每个块存储 url、title、postDate、heading。这样机器人在回答时可以准确引用来源,用户也能追溯原文。对于需要引用出处的场景,这比单纯提高召回率更有价值。

分块(chunking) 是把长文档切成适合嵌入与检索的小片段的过程,块的大小与边界直接决定检索精度。块太大,向量会混合多个主题、语义被平均化;块太小,则丢失上下文、答案不完整。常见策略包括固定长度切分、按段落/句子边界切分,以及本文采用的“标题感知”切分——把最近的 Markdown 标题作为上下文前缀带入每个块,使孤立片段也能表明自己属于哪个章节。块间重叠(overlap)用于防止关键句恰好被切在边界处而两边都不完整。配合 url、title、heading 等元数据,检索命中后模型能准确标注出处,实现可溯源的问答。

第五步:增量更新保持新鲜

Substack 归档接口默认按最新优先返回。每天跑一次增量任务时,只需要从第一页开始翻,直到遇到已见过的文章 ID 就停止。这样不用全量重抓,成本可控,新文章也能及时进入知识库。

对于个人知识库或小型研究代理,这套管道的运行成本很低;对于多出版物聚合场景,可以按域名分别建索引,再在检索层做融合。

现成工具与参考实现

如果不想自己写代码,原文提供了两个入口:

Apify 版本适合快速验证想法,自己写管道则更适合深度定制分块逻辑和元数据字段。

这套管道适合谁

做 AI 研究代理、行业情报机器人、个人知识库的开发者都能直接复用。Substack 的内容质量加上干净的结构化管道,比抓取通用网页做 RAG 的噪音低很多。唯一需要守住的边界是付费内容——只用元数据,不碰正文,这既是合规要求,也是对创作者的尊重。

你正在用什么内容源构建 RAG 知识库?欢迎在评论区交流。

#Substack RAG#Substack API#通讯转Markdown#RAG知识库构建#turndown HTML转Markdown#Substack爬取#RAG分块策略
分享:

相关推荐