🎯 一款可定制、具备反检测功能的云浏览器,由自主研发的 Chromium驱动,专为网页爬虫AI 代理设计。👉立即试用
返回博客

为RAG清理网页文本:获取、提取和分块管道

Isabella Garcia
Isabella Garcia

Web Data Collection Specialist

10-Jun-2026

主要摘录:

  • RAG质量即语料质量。 检索答案的好坏取决于你索引的文本——大多数管道故障都可以追溯到永远没有渲染的页面、被嵌入的导航元素或在思考中断裂的文本块。
  • 获取是一个不可靠的阶段。 现代页面是由JavaScript渲染并经过机器人检查的;普通的HTTP GET请求返回的是一个空壳或一个挑战页面,这些垃圾默默流入你的向量存储。
  • 一次POST请求返回渲染页面。 Scrapeless网络解锁器接受一个URL,并返回完全渲染的HTML,格式为{"code": 200, "data": "<html…>"}——渲染和反机器人处理在服务器端进行。
  • 提取是减法。 在读取文本之前去掉脚本、样式、导航和页脚;剩下的就是值得嵌入的散文。
  • 重叠分块,保留来源。 固定单词窗口与重叠保留跨界限的上下文,每个块都应携带其来源URL——没有来源的检索无法审计。
  • 免费开始。 新的Scrapeless账户包括免费试用积分——请在app.scrapeless.com注册。

管道一览

RAG系统检索文本块并将其馈送到模型中;下游的一切都继承索引中输入的内容。本指南从头到尾构建了摄取端:

  1. 获取 — 通过网络解锁器为URL列表提取完全渲染的HTML,以便JavaScript构建的页面和经过机器人检查的网站返回真实的内容。
  2. 提取 — 去除页面的外观保留散文。
  3. 分块 — 按照重叠的单词窗口将内容拆分,保留来源,准备好用于任何嵌入模型和向量存储。

输出为corpus.jsonl:每行一个块,包含其来源URL和位置——每个嵌入工作流接受的中立格式。阶段2和3为纯转换;只有阶段1接触网络。


为什么获取阶段首先出故障

有三种故障模式主导网页文本摄取,而这三种故障在检索质量下降之前都是隐形的:

  • 客户端渲染。 普通GET返回的HTML是一个加载器壳;文章稍后通过JavaScript到达。你的提取器读取的是一个空的<div id="root">,并且没有索引任何内容。
  • 反机器人插页。 挑战页面返回HTTP 200以及“检查您的浏览器”的文本——这段文本嵌入效果良好且检索时信心十足。
  • 软404。 渲染一个带样式的“未找到”页面的死链接,状态仍然是200。

解决办法是通过能够在服务器端渲染和清除这些层的基础设施进行获取。通用抓取API网络解锁器正是如此:每个URL一次POST,返回渲染的HTML。


前提条件

  • 一个Scrapeless账户和API密钥——请在app.scrapeless.com注册。
  • Python 3.10+,并安装requestsbeautifulsoup4库。
  • 一个你有权摄取的URL列表(见下方的来源说明)。
bash Copy
export SCRAPELESS_API_KEY=your_api_token_here

阶段1 — 获取渲染页面

每个URL对解锁器端点进行一次POST请求。响应为JSON,其中data包含渲染的文档:

python Copy
# fetch.py — URL列表 -> pages/*.html(完全渲染)
import os
import pathlib

import requests

ENDPOINT = "https://api.scrapeless.com/api/v1/unlocker/request"
HEADERS = {
    "Content-Type": "application/json",
    "x-api-token": os.environ["SCRAPELESS_API_KEY"],
}

URLS = [
    "https://www.scrapeless.com/zh/blog/best-llm-scrapers-2026",
    "https://www.scrapeless.com/zh/blog/google-ai-overview-scraper-api-2026",
]

pathlib.Path("pages").mkdir(exist_ok=True)
for url in URLS:
    resp = requests.post(
        ENDPOINT,
        headers=HEADERS,
        json={
            "actor": "unlocker.webunlocker",
            "input": {"url": url, "type": "html", "redirect": True, "method": "GET"},
        },
        timeout=120,
    )
    resp.raise_for_status()
    html = resp.json()["data"]
    name = url.rstrip("/").rsplit("/", 1)[-1] + ".html"
    pathlib.Path("pages", name).write_text(html, encoding="utf-8")
    print(f"{url} -> pages/{name} ({len(html):,} 字节)")

成功的获取结果为每篇文章页面提供数百千字节的渲染文档。只有几千字节通常意味着是壳或插页——在它到达索引之前值得检查。

在免费计划上获取你的API密钥:app.scrapeless.com


阶段2和3 — 提取散文、保留来源进行分块

提取即是减法:移除那些从不属于散文的元素,然后从剩下的文本中读取内容。分块是一个固定的重叠单词窗口,每个块保持其源 URL 和位置:

python Copy
# build_corpus.py — pages/*.html -> corpus.jsonl (带有来源的块)
import json
import pathlib

from bs4 import BeautifulSoup

CHUNK_WORDS = 220      # 窗口大小
OVERLAP_WORDS = 40     # 携带到下一个块

STRIP_TAGS = ["script", "style", "noscript", "nav", "header", "footer", "aside", "form", "svg"]


def extract_text(html: str) -> str:
    soup = BeautifulSoup(html, "html.parser")
    for tag in soup(STRIP_TAGS):
        tag.decompose()
    root = soup.find("article") or soup.find("main") or soup.body or soup
    text = root.get_text(" ", strip=True)
    return " ".join(text.split())


def chunk(words: list[str]):
    step = CHUNK_WORDS - OVERLAP_WORDS
    for start in range(0, max(len(words) - OVERLAP_WORDS, 1), step):
        yield start, " ".join(words[start:start + CHUNK_WORDS])


total = 0
with open("corpus.jsonl", "w", encoding="utf-8") as out:
    for page in sorted(pathlib.Path("pages").glob("*.html")):
        text = extract_text(page.read_text(encoding="utf-8"))
        words = text.split()
        for start, body in chunk(words):
            out.write(json.dumps({
                "source": page.stem,
                "word_offset": start,
                "n_words": len(body.split()),
                "text": body,
            }) + "\n")
            total += 1
        print(f"{page.name}: {len(words):,} words")

print(f"{total} chunks -> corpus.jsonl")

输出结果,每个块一行:

json Copy
// 示例样本 — 来自实时 build_corpus.py 运行的架构;文本已缩略
{
  "source": "best-llm-scrapers-2026",
  "word_offset": 180,
  "n_words": 220,
  "text": "…演员返回答案及其参考文献作为结构化字段…"
}

从这里开始,任何嵌入工作流程将接管:读取 corpus.jsonl,嵌入 text,将向量与 sourceword_offset 一同存储为元数据。来源字段能够让你追溯到糟糕检索的页面及其来源位置。


负责任的来源

训练或检索语料库的法律权重来源于其来源。仅获取你有权使用的公共可访问页面;在将其添加到 URL 列表之前,检查每个站点的服务条款和机器人指令;保持每个主机的请求量适中;并将版权文本视为可检索且需注明出处的内容,而不是再发布。当语料库包含第三方内容时,保持 source 字段完整是引用与复制之间的区别。


常见问题

问:为什么不直接对 URL 使用 requests.get

对于静态页面有效。对于使用 JavaScript 渲染或需要机器人检查的站点,它会返回外壳和插页,这会悄悄破坏索引 — 解锁器正是为了解决这些问题而存在。

问:块应该有多大?

这里的 220/40 窗口是句子变换器类嵌入模型的合理默认值。根据你模型的上下文和检索粒度进行调整;保持一些重叠,以便跨越边界的想法能够保留下来。

问:我怎么能在内容到达索引之前检测到糟糕的提取?

大小和内容检查:渲染的文章页面很大(示例中的页面通常在几百千字节),从应该是文章的页面中提取下来的文本低于几百字是一种值得记录的红旗。

问:这个方法能提取重度反机器人供应商后面的页面吗?

解锁器的任务是在服务器端清除反机器人层。当特定页面仍然无法被清除时,将其视为不可提取,并将其排除 — 丢失的页面是可恢复的,而被污染的索引则不可恢复。

问:我需要代理吗?

不需要。出站和渲染是在演员内部处理的;你发送的 POST 是整个集成。

问:嵌入和向量存储什么时候介入?

corpus.jsonl 下游,使用你已经在使用的任何堆栈。这个管道故意在干净的、带有来源标签的块处停止 — 这是每个嵌入工具所接受的格式。


结论:干净进,干净出

摄取管道简化为三个简短的文件:通过解锁器获取渲染的 HTML,删除多余的成分,进行重叠和来源分块。没有任何事情是华丽的,但所有这些决定了检索的答案是来自真正的文章文本,还是来自悄悄滑入索引的加载器外壳。将 URL 列表指向你的助理应该知道的来源,安排运行,并保存原始页面 — 它们是审计追踪。

准备构建你的 RAG 摄取管道?

加入我们的社区,申请一个免费计划,并与构建数据管道的开发者联系:Discord · Telegram
app.scrapeless.com 注册以获取免费试用积分,并将抓取阶段指向您的检索语料库所需的页面。请参阅 定价 以获取当前价格层级。

在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。

最受欢迎的文章

目录