读取一个网页并快速写出摘要不是难事。每天对数百个页面做同样的事,则是另一回事,而这恰恰是语言模型擅长的工作。难点从来不在于摘要本身,而在于如何从反爬的页面中提取干净、可读的文本,再将超过模型上下文窗口的文本输入模型而不丢失主线。

本指南端到端地介绍了这两个环节。你将使用 Crawlbase Crawling API 以干净的 Markdown 格式采集页面,然后用大语言模型对其进行摘要;当页面过长无法一次完成时,你将对其分块并执行 map-reduce 摘要,确保内容不遗漏。这里的所有内容都是针对公开网页内容的可运行 Python 代码。最终,你将拥有一个小型流水线,能将一批 URL 转化为简洁、一致的摘要,可供存储、检索或纳入报告。这就是使用 Crawlbase 和 AI 汇总网页数据的核心方法。

为什么要用 Crawlbase 和 AI 汇总网页数据

查看单个页面只能告诉你一份文档此刻的内容。价值体现在批量处理时:追踪一组竞品页面随时间的变化、将一批文章精炼为每日摘要,或将大量产品和评论页面浓缩成人们真正愿意阅读的几行话。大语言模型快速且一致,它对每篇文档应用相同的标准,到第两百篇时也不会疲倦。

然而,模型的输出质量取决于你输入的文本质量,而这正是大多数"AI 摘要"项目悄然失败的地方。现代页面 JavaScript 密集,被导航栏、广告、Cookie 横幅和样板内容层层包裹,许多还会直接封锁自动化流量。将原始 HTML 输入模型,你消耗的 token 是在摘要标签和菜单,而非内容。解决方案是将采集与摘要分离:让 Crawlbase 处理渲染、解封和干净提取,让模型做它擅长的事。关于提取步骤的深入介绍,参阅 AI 数据提取的工作原理

流水线的整体结构

整个流程分为两个阶段,保持两者分离才能让整套系统易于维护。

  • 采集。 Crawling API 通过可信 IP 获取每个 URL,在需要时渲染 JavaScript,并以干净的 Markdown 而非原始 HTML 返回内容。这意味着你输入模型的文本已经去除了导航、脚本和样式。
  • 摘要。 大语言模型读取 Markdown 并返回简短摘要。对于能放入模型上下文窗口的页面,这是一次调用。对于长页面,你将文本分成多个块,分别摘要,再对摘要进行汇总。最后这个模式就是 map-reduce。

向 Crawlbase 请求 Markdown 而非 HTML 的意义远比看上去大。Markdown 保留了标题、列表和结构,同时去除了噪声,让模型将上下文预算花在语义内容上。关于这个选择的更多讨论,参阅 适合大语言模型的 Markdown 网络爬取

优先选择 Markdown 而非原始 HTML

当你传入 format=markdown(或爬虫的 markdown 选项)时,Crawling API 可以以 Markdown 格式返回页面。在摘要场景中,始终优先选择 Markdown 而非原始 HTML。原始 HTML 会将 token 消耗在模型不需要的标签和内联样式上,多余的噪声会明显降低摘要质量。Markdown 保留了有助于模型理解的结构,并去除其余内容。

配置项目

你需要 Python 3 和两个账户:用于获取 token 的免费 Crawlbase 账户,以及用于模型的 OpenAI 账户。先创建 Crawlbase 账户;注册时你将获得 1,000 次免费 API 请求,添加账单信息后可解锁剩余的 10,000 次免费额度,足以跟随本指南完成所有操作。从账户文档页面复制你的普通请求 token,并从 OpenAI 获取一个 API 密钥。

然后创建项目文件夹并安装所需库。

bash
python --version

mkdir web-summarizer && cd web-summarizer
python -m venv .venv && source .venv/bin/activate
pip install requests openai tiktoken

三个依赖各司其职:requests 调用 Crawling API,openai 是模型客户端,tiktoken 计算 token 数量,以便判断页面是否过大而无法单次调用。将你的两个密钥设置为环境变量,使其不出现在代码中。

bash
export CRAWLBASE_TOKEN="your_crawlbase_normal_token"
export OPENAI_API_KEY="your_openai_api_key"

第一步:以干净的 Markdown 格式获取页面

从采集开始。你向 Crawling API 发送目标 URL 和 format=markdown 选项,它返回已转换为 Markdown 的页面。下面的函数封装了该调用,检查上游状态,并只返回 Markdown 主体,使流水线的其余部分永远看不到原始 HTML。

python
import os
import requests

CRAWLBASE_TOKEN = os.environ["CRAWLBASE_TOKEN"]
API_ENDPOINT = "https://api.crawlbase.com/"

def fetch_markdown(url: str) -> str:
    params = {
        "token": CRAWLBASE_TOKEN,
        "url": url,
        "format": "markdown",
    }
    response = requests.get(API_ENDPOINT, params=params, timeout=90)
    response.raise_for_status()
    return response.text

if __name__ == "__main__":
    markdown = fetch_markdown("https://www.crawlbase.com/blog/")
    print(markdown[:800])

运行后你会得到 Markdown 格式的文章文本,标题和列表完整保留,页面外框已去除。如果你的目标通过 JavaScript 渲染内容,同样的调用使用 JavaScript token 而非普通 token 即可,页面会在真实浏览器中渲染后再转换。只需换一个 token,你就可以在不修改其他代码的情况下对单页应用内容进行摘要。

Crawlbase Crawling API

干净的文本输入,优质的摘要输出。Crawling API 渲染 JavaScript,通过住宅 IP 轮换突破封锁,并在单次调用中以可立即摘要的 Markdown 格式返回每个页面,让模型永远看不到导航栏或 Cookie 横幅。从免费套餐开始,指向任何公开 URL。

第二步:在单次调用中摘要短页面

当页面能轻松放入模型上下文窗口时,摘要只需一次请求。下面的函数接受 Markdown 文本和一段简短指令,以较低的温度发送给模型以保证一致性,并返回摘要字符串。在这里保持较低温度很重要:你希望相同的输入在多次运行中产生稳定的输出,而非创意性的变化。

python
from openai import OpenAI

client = OpenAI()  # reads OPENAI_API_KEY from the environment
MODEL = "gpt-4o-mini"

def summarize(text: str, instruction: str) -> str:
    prompt = f"{instruction}\n\n---\n\n{text}"
    response = client.chat.completions.create(
        model=MODEL,
        messages=[{"role": "user", "content": prompt}],
        temperature=0.2,
    )
    return response.choices[0].message.content.strip()

SUMMARY_PROMPT = (
    "Summarize the following web page in 4-6 sentences. "
    "Lead with the main point, then the key supporting facts. "
    "Ignore navigation, ads, and boilerplate."
)

if __name__ == "__main__":
    page = fetch_markdown("https://www.crawlbase.com/blog/")
    print(summarize(page, SUMMARY_PROMPT))

这就是普通文章的完整顺利路径。获取 Markdown,附上指令发送,打印结果。模型负责语言工作,Crawlbase 负责数据工作。唯一横亘在这与不适合单次处理的页面之间的,是长度问题,这就是下一步要解决的。

第三步:通过分块处理长页面

每个模型都有上下文窗口,即单次调用能读取的文本量的硬性限制。长篇文章、文档页面和论坛帖子可能超过这个限制,超过后 API 会拒绝请求。解决方案是将文本分割成各自能放入的块,并留有一小段重叠,使在边界处被截断的句子仍能在其中一个块中完整出现。

使用 tiktoken 而非字符来计算 token 数量,因为限制是以 token 为单位的。下面的函数遍历 token 列表,将其切片为固定大小的窗口。

python
import tiktoken

encoder = tiktoken.encoding_for_model("gpt-4o-mini")

def chunk_text(text: str, max_tokens: int = 2000, overlap: int = 150):
    tokens = encoder.encode(text)
    chunks = []
    start = 0
    while start < len(tokens):
        end = start + max_tokens
        window = tokens[start:end]
        chunks.append(encoder.decode(window))
        start = end - overlap
    return chunks

现在每个块都是一段独立的文本,小到足以单独摘要。对于现代拥有大上下文窗口的模型,2,000 token 的 max_tokens 为提示词和响应留下了充裕空间;对于较小的模型可适当降低。重叠确保两个块之间的边界句子不会丢失。以 Crawlbase 输出的干净 Markdown 作为输入,这些块都是纯内容,从而减少了块的数量并保持摘要的主题聚焦。

第四步:使用 map-reduce 合并块摘要

分块给你提供了多个片段;map-reduce 将它们重新合并为一个答案。该模式分两个阶段。在 map 阶段,你独立摘要每个块,得到一组局部摘要。在 reduce 阶段,你将这些局部摘要拼接起来,再次摘要,得到单个最终摘要。如果合并后的局部摘要本身过长,你再次 reduce,重复直到只剩一个摘要。

python
MAP_PROMPT = (
    "Summarize this section of a longer document in 3-4 sentences. "
    "Keep concrete facts, names, and numbers."
)

REDUCE_PROMPT = (
    "The following are summaries of consecutive sections of one document. "
    "Combine them into a single coherent summary of 5-7 sentences, "
    "removing repetition and keeping the overall narrative."
)

def summarize_long(text: str) -> str:
    chunks = chunk_text(text)

    if len(chunks) == 1:
        return summarize(chunks[0], SUMMARY_PROMPT)

    partials = [summarize(c, MAP_PROMPT) for c in chunks]
    combined = "\n\n".join(partials)

    while len(encoder.encode(combined)) > 2000:
        partials = [summarize(c, MAP_PROMPT) for c in chunk_text(combined)]
        combined = "\n\n".join(partials)

    return summarize(combined, REDUCE_PROMPT)

这个函数现在可以处理任意长度。短页面只需一次调用并立即返回。长页面会被 map、reduce,必要时再次 reduce,循环保证最终输入始终能放入上下文窗口。分开使用的 map 和 reduce 提示词很重要:map 提示词要求输出富含事实的局部摘要,使细节在第一轮得以保留;reduce 提示词要求输出清晰的叙述,使最终摘要读起来像一个整体,而非拼凑的列表。

第五步:对多个 URL 批量运行

现在两个阶段组合成了一个小型流水线。给它一批 URL,以 Markdown 格式获取每个,用能感知长度的函数摘要每个,并收集结果。用 try/except 包裹每个 URL,这样一个失败的页面不会拖垮整批,你就拥有了一个可以指向任意数据源的工具。

python
import json

URLS = [
    "https://www.crawlbase.com/blog/",
    "https://www.crawlbase.com/blog/ai-data-extraction-how-it-works/",
]

def run_pipeline(urls):
    results = []
    for url in urls:
        try:
            markdown = fetch_markdown(url)
            summary = summarize_long(markdown)
            results.append({"url": url, "summary": summary})
        except Exception as error:
            print(f"Skipped {url}: {error}")
    return results

if __name__ == "__main__":
    output = run_pipeline(URLS)
    print(json.dumps(output, indent=2))

输出是一个包含 urlsummary 键值对的 JSON 数组,可以写入文件、推送到数据库或渲染为摘要。以下是返回结果的精简示例:

json
[
  {
    "url": "https://www.crawlbase.com/blog/",
    "summary": "The Crawlbase blog covers web scraping, proxies, and data extraction, with hands-on tutorials for engineers. Recent posts focus on rendering JavaScript sites, avoiding blocks, and turning pages into clean structured data."
  },
  {
    "url": "https://www.crawlbase.com/blog/ai-data-extraction-how-it-works/",
    "summary": "The article explains how AI models extract structured fields from messy web pages, contrasting rule-based scrapers with model-driven extraction that adapts to layout changes."
  }
]

生产环境实用建议

缓存已获取的 Markdown

采集与摘要的失败原因不同,因此不要将两者耦合。在获取每个页面的 Markdown 后,立即以 URL 为键将其保存到磁盘。当你想用不同的提示词或模型重新运行时,直接从缓存摘要,无需重新爬取,既更快,也不会在已有页面上消耗 API 额度。

控制频率并重试

两个 API 都可能对紧密的循环进行限速。在 URL 之间加入短暂的休眠,并在模型调用外围加上带退避的重试,这样短暂错误就不会丢失一个页面。Crawling API 会为你处理 IP 轮换和解封,因此这里需要的节流力度较轻,但做一个礼貌的客户端仍然值得。

固定提示词和模型

可复用、版本化的提示词是使摘要在多次运行中保持一致的关键。将 map 和 reduce 提示词集中在一处,固定模型名称,保持较低温度。当你修改其中任何一个时,请将其视为对输出的更改,因为事实确实如此。

根据页面类型选择工具

对静态页面使用普通 token,对在浏览器中渲染内容的单页应用使用 JavaScript token。如果你需要的是价格、标题和评分等结构化字段而非叙述文字,可以使用 Crawling API 获取结构化 JSON,然后对其进行摘要。如果你要将这套流程接入 agent 或基于 MCP 的工作流,Web MCP 会将同样的爬取和提取能力作为工具暴露给你的模型。关于完整的 agent 构建,使用 LangChain 和 Crawlbase 构建 AI 数据流水线将这个方向推得更远。

回顾

核心要点

  • 将采集与摘要分离。 Crawlbase 获取干净的文本,模型完成语言工作。保持两个阶段分离是流水线易于维护的关键。
  • 获取 Markdown 而非 HTML。 传入 format=markdown,让模型将上下文花在内容上,而非导航栏和脚本。
  • 先计数 token,再分块。 使用 tiktoken 将长页面分割为各自适配上下文窗口的重叠窗口。
  • map-reduce 可扩展到任意长度。 摘要每个块,再摘要所有摘要,重复直到只剩一个。
  • 缓存并固定以备生产使用。 保存已获取的 Markdown,版本化你的提示词,固定模型,保持较低温度以获得一致的输出。

常见问题

为什么摘要时要获取 Markdown 而非原始 HTML?

原始 HTML 充斥着标签、脚本、内联样式、导航和广告,这些对摘要毫无意义,却仍会消耗 token。Crawling API 可以以 Markdown 格式返回页面,保留标题、列表和正文,同时去除噪声。这意味着模型将有限的上下文用于实际内容,摘要质量更高,每个页面支付的 token 也更少。

什么是 map-reduce 摘要,什么时候需要它?

map-reduce 是处理文本过长而无法单次模型调用完成的两阶段模式。在 map 阶段,你独立摘要文档的每个块;在 reduce 阶段,你将这些局部摘要合并为一个最终答案。当页面超过模型的上下文窗口时就需要它。短页面直接进入单次调用,这就是为什么示例会先检查块的数量。

如何选择块大小?

以 token 而非字符为单位设置块大小,并为提示词和响应留出余量。对于拥有大上下文窗口的现代模型,2,000 token 的块大小效果良好;对于较小的模型可适当降低。加入约 100 到 200 token 的小重叠,使在块边界处被截断的句子仍能在其中一个块中完整出现。使用 tiktoken 并匹配你的模型使用的编码来计算 token。

我可以对 JavaScript 渲染的页面进行摘要吗?

可以。在 Crawling API 调用中将普通 token 替换为 JavaScript token 即可。它会在将页面转换为 Markdown 之前在真实浏览器中渲染页面,因此单页应用的内容在模型读取时已经完整呈现。流水线的其余部分,即分块和 map-reduce,完全不需要更改。

我需要付费的 Crawlbase 或 OpenAI 账户才能跟随本指南吗?

不需要。Crawlbase 注册即提供 1,000 次免费 API 请求,添加账单信息可解锁剩余的 10,000 次免费额度,足以端到端地测试本指南。OpenAI 按 token 计费,像 gpt-4o-mini 这样的小模型摘要成本很低。在扩大规模之前,两者都可以从免费或低成本套餐开始。

我可以使用不同的模型或提供商吗?

可以。流水线对模型只有两个依赖:一个接受提示词并返回文本的聊天式调用,以及一个用于分块的 token 计数器。将 summarize 函数中的客户端替换为你偏好的任意提供商,并更新 tiktoken 编码以匹配该模型。采集阶段和 map-reduce 逻辑完全不变。

开始构建

大规模爬取任何站点,无需与基础设施对抗。

Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。

自助开通 · 无需销售通话 · 提供企业级爬取量