数据抓取

LLM提取与选择器对比:何时该让模型来读取页面

我们让一个大语言模型从18个实时新闻页面中提取字段,并将结果与页面自身的结构化数据进行对比评分。准确性、成本,以及何时该使用它。

Chris Collins

Chris Collins

2026年9月28日 · 3 分钟阅读

二十年来,从网页中提取数据意味着编写选择器:找到元素、抓取文本,等网站改版时再修一遍。大语言模型提供了一种不同的方案。把页面交给模型,描述你想要的字段,就能得到结构化数据,不用写选择器,也不会因为改版而失效。

这笔交易是真实的,但它有代价、有速度、也有失败模式,这三者都值得在围绕它重建流水线之前先测量一下。所以我们测量了。本文报告了一次在真实页面上进行的小型、诚实的测试,那些未命中的结果揭示了什么,规模化运行的成本是多少,以及模型在提取流水线中应该处于什么位置。

关键结论

  • 在18篇真实新闻文章上,一个大型Claude模型准确提取了全部标题,17/18的日期正确,14/18的作者列表正确,评分基准是每个页面自身的结构化数据。
  • 大多数”未命中”并非模型的错误。在每一个作者不一致的页面上,结构化数据都保留了一个通用的占位符;其中两个页面上,模型报告的实际上是页面上真正显示的署名。
  • 模型没有编造任何内容:它返回的每一个标题和作者都出现在页面文本中。但它确实犯了一个真正的错误:把一位摄影师标为了作者。
  • 按标价计算,每页成本约1.9美分,中位耗时2.4秒。而解析结构化数据几乎不花钱,耗时以毫秒计。
  • 在选择器维护成本高的场景下使用模型,比如涉及许多不同的网站模板,或者没有结构化来源的字段,并对其返回的一切进行验证。

我们测试了什么

设置值
页面来自一家主要新闻出版商栏目首页的18篇真实文章,采集于2026年9月28日
模型输入页面的可见文本,包括导航栏和其他页面装饰元素,平均约8,700个字符
字段标题、页面上显示的发布日期、作者姓名
模型Claude Opus 5,低努力程度,使用JSON schema约束输出
参考答案同一页面自身的JSON-LD结构化数据
评分方式标题和日期须完全匹配;作者列表须作为集合匹配

参考答案刻意采用了停止解析HTML一文中所涉及的那类数据:出版商为搜索引擎嵌入的结构化数据。它通常是正确的。但结果表明,并非总是如此。

结果

指标结果
标题正确18/18
日期正确17/18
作者正确14/18
提取值在页面文本中找到18/18标题,18/18作者列表
每页token数输入约3,380,输出约66
延迟中位数2.4秒,范围1.8至10.6秒
本次运行成本按标价计算$0.33,约每页1.9美分

未命中之处才是有意思的部分

标题层面的数字低估了模型,也高估了参考答案。逐一看每处分歧:

  • 一篇专栏评论。 结构化数据把作者列为一个通用的员工记者占位符。页面上可见的署名标注的是实际专栏作者。模型返回的是那位专栏作者。
  • 一篇通讯社稿件。 结构化数据同样给出了通用占位符;而可见的署名写的是”Staff and agencies”。模型返回的是页面上实际显示的内容。
  • 一个混入数据集的新闻通讯订阅页面。 它没有显示作者也没有日期。结构化数据却仍然给出了一个通用作者和一个日期;模型则把这两个字段都留空,而不是去编造。
  • 一篇图片专题报道。 结构化数据给出了相同的通用占位符。页面把这些照片的署名标注给了一位具名摄影师,模型把这位摄影师作为作者返回。这是一个真正的错误。

因此,在这四个存在分歧的页面中,两个反映的是参考答案的准确性不如可见页面本身,一个是模型正确地拒绝了猜测,一个是真正的错误。这改变了值得追问的问题。结构化数据是一个强有力的默认选项,但它并非绝对真相,而一个阅读可见页面的模型能够发现它出错的地方,正如它偶尔也会看错自己所见的内容一样。

规模化运行的成本

这次测试对18个页面花费了$0.33,按Claude Opus 5的标价计算,每百万输入token为$5,每百万输出token为$25。这大约相当于每页1.9美分,折算到每百万页大约为$18,600。Anthropic的Batch API能将这些token价格降低一半,适用于可以等待的工作。更小的模型每个token的价格更便宜,Claude Haiku 4.5的标价为$1和$5,但我们没有在此测试它们,它们在你的页面上的准确率是需要测量的,而不能想当然。

延迟同样重要。每页中位数2.4秒,偶尔出现异常值,这对于监控和数据丰富化任务来说没问题,但对高吞吐量的爬取来说是一个真实的限制。解析JSON-LD或运行选择器耗时以毫秒计,除了抓取本身几乎不花钱。这些提取成本要叠加在采集成本之上,这也是为什么每条干净记录的成本应该把两者都计入。

何时用哪种方法

场景最佳方法
页面在JSON-LD或嵌入式JSON中发布了这些字段解析结构化数据
来自一个或少数几个网站模板的高吞吐量场景选择器或结构化数据,并加以验证
众多不同网站,每个都有自己的模板使用模型,并验证,也可以让模型生成选择器后再复用
仅存在于文字叙述中的字段,比如条款、资格条件或规格说明使用模型
结构化数据在某页面验证失败将模型作为后备方案
低吞吐量、高价值、布局频繁变化的场景使用模型

最强的流水线会把两者结合起来。先解析结构化数据,当必需字段缺失或验证失败时再退回使用模型,并记录每条记录是由哪种方法生成的,这与结构化数据一文中描述的后备链模式相同。当一个网站模板覆盖了成千上万个页面时,让模型一次性提出选择器方案,然后在每个页面上低成本地运行这些选择器,同时让模型待命以应对选择器失效的情况,这样做也是值得的。

安全地使用模型

四个做法能让模型提取从令人印象深刻变得可靠。

约束输出。 JSON schema能让模型返回你要求的字段和类型;但仍要检查停止原因,因为一个被拒绝或被截断的响应是没有内容可解析的。告诉模型在页面没有显示某字段时把它留空;在我们的测试中,它正是这样做的。

检查关联性。 每一个理应出现在页面上的提取字符串,比如姓名、标题或产品名称,都应该能在页面文本中找到。这是一个成本低廉的检查方式,能够抓出编造的内容。但正如摄影师那个案例所示,它无法抓出一个真实姓名被错误关联到了错误字段的情况,所以它是一个过滤器,而不是一个证明。

抽样进行人工复核。 每周对一小批随机样本进行评分,与人工阅读页面的结果对比,并按字段和网站逐一跟踪准确率随时间的变化。

把页面文本当作不可信的输入。 页面是别人写的。仅将模型用于提取网页内容,绝不让页面内容触发任何操作,并将模型的指令与页面内容分开保存。

以下是我们使用的提取调用代码,紧接着是关联性检查:

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const SCHEMA = {
  type: "object",
  properties: {
    headline: { type: "string" },
    date_published: { type: "string", description: "YYYY-MM-DD, as shown on the page" },
    authors: { type: "array", items: { type: "string" } },
  },
  required: ["headline", "date_published", "authors"],
  additionalProperties: false,
};

export async function extractArticle(pageText) {
  const response = await client.beta.messages.create({
    model: "claude-opus-5",
    max_tokens: 2000,
    betas: ["server-side-fallback-2026-07-01"],
    fallbacks: "default",
    output_config: { effort: "low", format: { type: "json_schema", schema: SCHEMA } },
    messages: [{
      role: "user",
      content:
        "Below is the visible text of a news article web page, including navigation and other page furniture. " +
        "Extract the article's headline exactly as written, its publication date as shown on the page (YYYY-MM-DD), " +
        "and the author names. Leave a field empty if the page does not show it.\n\n<page>\n" + pageText + "\n</page>",
    }],
  });
  if (response.stop_reason === "refusal") return null;
  const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
  return { record: JSON.parse(text), usage: response.usage };
}

// Grounding check: every extracted string must actually appear in the page text.
export function grounded(record, pageText) {
  const norm = (s) => s.replace(/[‘’]/g, "'").replace(/[“”]/g, '"').replace(/\s+/g, " ").toLowerCase();
  const page = norm(pageText);
  return {
    headline: page.includes(norm(record.headline)),
    authors: record.authors.every((a) => page.includes(norm(a))),
  };
}

输入与模型同样重要。模型只能提取页面实际包含的内容,因此一个被拦截的页面、一个同意墙或一个空壳页面都会产出一个对错误内容自信满满的提取结果。在提取之前先验证你抓到的内容,正如静默失败率一文所述,并从你需要的那个版本的页面所在的市场进行抓取。

本次测试的局限

来自一家出版商的十八个页面样本量很小,选择它是为了诚实而非追求确定性。新闻文章也是一个相对容易的场景:标题清晰、署名可见、日期标注良好。带有变体、价格和库存状态的商品页面,或使用其他语言的页面,表现会有所不同。我们只测试了一个模型的一个努力程度设定。这个方法很容易在你自己的页面上重复验证,而你自己的页面才是唯一重要的基准。

结论

一个阅读页面的模型准确提取了全部标题,没有编造任何内容,而在若干情况下,它比页面自身的结构化数据更忠实地反映了页面内容。它也曾一次把作者标错,成本是几美分而非几分之一美分,耗时是几秒而非几毫秒。

这使它成为一个强有力的工具,适用于选择器处理不好的那部分提取工作:众多模板、仅存在于文字叙述中的字段,以及结构化数据失败时的后备方案。但在结构化数据存在的地方,它并不能替代结构化数据。解析页面所发布的内容,在页面没有发布的地方求助于模型,对两者都进行验证,并记录你用的是哪一种方法。

来源与参考资料

  • Anthropic,定价。测试当时的模型与Batch API价格。
  • Schema.org,NewsArticle。
  • 测试由Shifter于2026年9月28日针对18篇可公开访问的新闻文章运行,使用上述代码完成。

准备好开始了吗?

试用 Shifter 住宅代理,205M+ 个 IP,195+ 个国家,低至 $0.10/GB。

立即开始