截至2026年10月7日,Shifter拥有规模最大的美国实时IP池,超过Oxylabs和Bright Data,而成本只是其一小部分。Shifter 现在拥有数量最多的美国活跃 IP 资源池

查看基准测试

找到页面背后的API:从JSON端点收集数据而非HTML

许多页面以JSON形式加载数据。如何找到承载数据的端点,为何它通常与页面的会话绑定,以及如何负责任地收集它。

工程 · 更新于2026年9月30日 · 3分钟阅读
61 请求数3.62 MB44.9 KB票价409直接调用
一个页面,61个请求。只有蓝色那个携带了票价信息。
数据抓取
本页内容
  1. 关键要点
  2. 我们测量了什么
  3. 找到承载数据的端点
  4. 你可以直接调用它吗?
  5. JSON 在存在时为何值得使用
  6. 这在各种替代方案中处于什么位置
  7. 负责任地采集
  8. 结论
  9. 来源与参考

打开一个现代网页,观察其网络活动,你常常会看到你想要的数据以 JSON 的形式先行到达,早于页面将其绘制出来。爬虫本来需要费力地从 HTML 中提取的价格、列表或结果,其实就存放在页面自己获取的、结构清晰的响应中。

从该响应中采集数据,而不是从渲染后的页面采集,可以做到体积更小、更稳定,也更容易解析。但这并不像复制一个 URL 那么简单,有两点会让多数团队感到意外:一个页面加载的 JSON 中,真正是数据的部分有多么少;以及数据端点在脱离调用它的页面之后,有多么频繁地拒绝工作。本指南展示如何找到正确的端点、我们在真实页面上测得了什么,以及如何在不越界的前提下从中采集数据。

关键要点

  • 页面加载的大多数 JSON 并不是数据。在某航空公司的航班搜索页面上,票价数据装在一个 44.9 KB 的响应中,约占该页面 JSON 总量的 8%,约占其 3.6 MB 总传输量的 1.2%。
  • 有些页面根本没有数据 API。在某国家气象部门的天气预报页面上,所有 JSON 响应都属于 cookie 同意工具,而预报数据本身在 HTML 里。
  • 通过在响应体中搜索页面上可见的某个值来找到端点,而不是靠猜测 URL。
  • 页面 API 通常与页面的会话绑定。该航空公司的票价端点在页面内可以工作,但直接调用时返回 HTTP 409。
  • 只使用页面为匿名访客调用的公开端点,按照普通浏览者的节奏访问,并且只要存在官方 API,就优先使用官方 API。

我们测量了什么

我们在 2026 年 9 月 30 日用真实浏览器加载了两个公开页面,并记录了每一个响应。

航空公司航班搜索天气预报
请求数6146
总传输量3.62 MB2.57 MB
JSON 响应15 个,534 KB3 个,1.04 MB
承载数据的 JSON 响应1 个,44.9 KB0
数据存放位置一个票价端点服务端渲染的 HTML

在该航空公司的页面上,最大的 JSON 响应根本不是票价。一个第三方功能开关(feature-flag)服务返回了同一个 97 KB 的响应四次,一个翻译语言包又添加了 92 KB。票价数据来自该航空公司自家预订 API 的一个单一响应。

在天气页面上,全部三个 JSON 响应都来自 consent 管理工具,其中一个是 860 KB 的供应商列表,而预报本身是在服务端被渲染进 HTML 的。“从 JSON 中采集”在这里将一无所获。

找到承载数据的端点

可靠的方法是搜索,而不是猜测。选取一个页面上可见的值,比如价格、产品名称或标识符,在真实浏览器中加载该页面,然后找出哪个 JSON 响应包含这个值。

手动操作的话,就是使用浏览器的开发者工具:打开 Network 标签,筛选出 Fetch/XHR,重新加载,然后在响应体中搜索该值。若要自动化,只需一段简短的脚本:

import { chromium } from 'playwright';

// Load a page once and report which JSON responses contain a value you can see on it.
export async function findEndpoint(url, needle, waitMs = 20000) {
  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();
  const hits = [];
  page.on('response', async (response) => {
    const type = response.request().resourceType();
    const contentType = response.headers()['content-type'] || '';
    if (!['xhr', 'fetch'].includes(type) || !contentType.includes('json')) return;
    try {
      const body = await response.text();
      if (body.includes(needle)) {
        hits.push({
          method: response.request().method(),
          status: response.status(),
          bytes: Buffer.byteLength(body),
          url: response.url(),
        });
      }
    } catch {
      // Some responses (redirects, aborted requests) have no readable body.
    }
  });
  // Analytics beacons keep many pages from ever going network-idle,
  // so wait for the DOM, then until a match appears or the time runs out.
  await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 90000 });
  for (let waited = 0; waited < waitMs && hits.length === 0; waited += 500) {
    await page.waitForTimeout(500);
  }
  await page.waitForTimeout(1000);
  await browser.close();
  return hits;
}

在该航空公司的航班页面上,给定页面上可见的某个票价,脚本从 61 个请求中恰好返回了一个匹配项:预订 API 的可用性(availability)响应。请注意其中的等待策略。我们最初的版本是等待页面进入网络空闲(network-idle)状态,但它从未达到过这个状态,因为分析类的信标请求一直在触发;结果在 90 秒后超时。先等待 DOM,再等待匹配出现的方式更可靠。

你可以直接调用它吗?

通常不行。当我们直接、脱离页面地请求同一个航空公司可用性端点时,来自六个不同国家的请求都同样返回了 HTTP 409,而页面本身加载它却毫无问题。页面 API 通常依赖于页面预先设置好的一些东西:会话 cookie、嵌入在 HTML 中的令牌、请求头,或者一连串在此之前发生的调用。

这就剩下两种方式:

  • 让页面自己发起调用。 在浏览器中加载页面,并在 JSON 响应到达时读取它,就像上面的脚本那样。你能获得干净的数据,而无需重建整个会话,代价是需要运行一个浏览器。
  • 只回放简单的公开端点。 有些端点只需要一个 URL 即可,无需其他。同一家航空公司还发布了一个公开的票价端点,能够响应普通请求,我们在一次单独的测试中用它来验证价格是否会因出口国家而不同。在端点能够如此简单地工作的情况下,这无疑是成本最低的选择。

不应该做的是绕过会话本身:收集令牌、伪造请求头,或者回放经过身份验证的调用,以获取网站并未向匿名访客开放的数据。这就是采集不再是”观察”的分界点。

JSON 在存在时为何值得使用

当数据确实以 JSON 形式到达时,收益是实实在在的:

  • 体积。 票价响应只有 44.9 KB,而完整页面是 3.6 MB。在按带宽计费的采集中,这个差距占了账单的绝大部分,正如每条干净记录的成本一文所展示的那样。
  • 结构。 字段以命名和类型化的形式到达,无需编写或维护任何选择器。
  • 完整性。 响应中携带的内容常常比页面展示的更多,比如标识符、全部票价等级或库存标记。

但代价同样真实存在。未文档化的端点会毫无预警地改变,而形态发生变化的响应会悄无声息地破坏你的解析器,这正是模式漂移(schema drift)监测存在的意义所在。路径中的版本号,比如该航空公司预订 API 中的 v4,只是稳定性的一个轻微信号,并不是一种承诺。

这在各种替代方案中处于什么位置

数据来源稳定性工作量适用场景
官方、文档化的 API最高最低始终优先检查
JSON-LD 或嵌入的页面状态高低页面本身发布了它,参见停止解析 HTML
页面自身的 JSON 端点中中数据在页面加载之后才出现,且端点是公开的
渲染后的 HTML 与选择器最低最高没有其他方式能承载数据
由模型阅读页面不定每个站点低,每个页面高模板众多的情形,如LLM 提取与选择器对比

负责任地采集

页面调用的端点是网站的一部分,网站的规则依然适用。只使用页面为匿名访客调用的端点,以普通浏览者的节奏发起请求,遵守 robots.txt 和网站条款,对于登录或令牌背后的任何内容,除非获得许可,否则一律不去触碰。如果网站提供官方 API,就使用它;它更稳定,也是网站已同意支持的方式。更广泛的原则见于robots.txt、AI 退出选项与预留信号一文。

结论

现代页面背后的数据常常以 JSON 形式到达,而当它如此到达时,从那里采集比解析 HTML 更小、更干净,也更容易维护。但要找到它需要搜索,而不是猜测:在我们测量的页面中,有用的 JSON 只是众多响应中的一个,而在其中一个页面上,它根本不存在。

在响应体中搜索一个你能看到的值,检查该端点是否能单独工作,在它不能单独工作时让页面自己去调用,并始终停留在网站向任何匿名访客所提供的范围之内。

来源与参考

  • 由 Shifter 于 2026 年 9 月 30 日使用上述代码加载并测量的页面。
  • Playwright,网络事件文档。

准备好开始了吗?

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

立即开始