登录

SDK 的设计形态

Node SDK 是对 API Reference 中所记录 HTTP API 的轻量封装。在原始 HTTP 调用中你会作为查询字符串附加的每个 Crawling API 参数,都可以通过 SDK 的 options 对象中的键来访问 —— 名称、默认值和行为都是一一对应的。SDK 不会新增任何参数,也不会隐藏任何参数。

相比直接使用 fetch / axios,使用它能获得:

  • URL 编码、参数校验和响应解析开箱即用 —— 代码读起来像产品代码,而非 HTTP 管道代码。
  • 同时支持 ESM(import { CrawlingAPI } from 'crawlbase')和 CommonJS(const { CrawlingAPI } = require('crawlbase'))。
  • 每个 Crawlbase API 对应一个客户端类,全部共享相同的构造函数 / 调用形态。
  • 合理的默认值(90 秒超时、自动解析 format=json 响应、UTF-8 解码),与大多数团队首次集成时手动配置的设置一致。

SDK 开源,采用 MIT 许可,欢迎在 github.com/crawlbase/crawlbase-node 提交社区 PR。

安装

npm 上的最新版本。在所有主流包管理器上支持 Node.js 16+。

npm install crawlbase

# Or via pnpm / yarn / bun
pnpm add crawlbase
yarn add crawlbase
bun add crawlbase

源码在 GitHub。欢迎提交 issue 和 PR。

身份验证

每个 Crawlbase API 都使用相同的 token 模型进行身份验证。同一个账号下有两种 token:

  • Normal Token (TCP) —— 用于静态 HTML、JSON 端点,以及任何不需要浏览器的场景。更快、更便宜。
  • JavaScript Token —— 用于 SPA、懒加载信息流,以及任何将内容隐藏在客户端渲染后的场景。使用 page_waitajax_waitscrollcss_click_selector 时必需。

在生产环境中请使用环境变量。SDK 本身不会读取环境变量 —— 这是有意为之,以便您完全掌控凭证的来源 —— 但惯用的写法是:

import { CrawlingAPI } from 'crawlbase';

// Pick the right token at instantiation; the SDK doesn't switch
// tokens per-call, so keep two clients if you alternate.
const api = new CrawlingAPI({ token: process.env.CRAWLBASE_TOKEN });
const js  = new CrawlingAPI({ token: process.env.CRAWLBASE_JS_TOKEN });

await api.get('https://github.com/anthropic');
await js.get('https://feed.example.com', { page_wait: 2000 });

完整的 token 模型和控制台位置请参见 Authentication 页面。

快速上手

从导入到爬取 HTML 只需三行代码。ESM 和 CommonJS 都可以:

// ESM
import { CrawlingAPI } from 'crawlbase';

const api = new CrawlingAPI({ token: 'YOUR_TOKEN' });
const res = await api.get('https://github.com/anthropic');

if (res.statusCode === 200) {
  console.log(res.body);
}

// CommonJS - same shape
// const { CrawlingAPI } = require('crawlbase');

在决定是否重试时,请基于 response.statusCode(SDK 与 Crawlbase 之间的 HTTP 状态码)和 response.headers.pc_status(Crawlbase 的判定结果 —— 见下方 Errors)进行分支处理。传入 { format: 'json' } 可获得 JSON 封装的响应,而不是原始页面内容。

所有 API 集成在一个包中

每个 Crawlbase API 都有对应的客户端类。相同的构造函数,相同的 get / post 方法。

import {
  CrawlingAPI,    // general-purpose page fetch
  ScraperAPI,     // parsed JSON for supported sites
  LeadsAPI,       // domain-scoped email extraction (legacy)
  ScreenshotsAPI, // screenshots of any URL
} from 'crawlbase';

const token = { token: 'YOUR_TOKEN' };

const crawl   = new CrawlingAPI(token);
const scraper = new ScraperAPI(token);
const leads   = new LeadsAPI(token);
const shots   = new ScreenshotsAPI(token);

// Push high-volume async jobs to the Enterprise Crawler via the
// Crawling API: api.get(url, { async: true, callback: '...',
// crawler: 'YourCrawler' }). See /docs/crawler for the queue
// workflow.

常见模式

JavaScript 渲染

对于 SPA、懒加载信息流以及初始 HTML 为空的页面,使用 JavaScript token 实例化客户端,并传入 page_waitajax_waitscrollcss_click_selector 的任意组合。考虑顺序:先固定等待,再等待网络空闲,然后滚动加载懒加载内容,最后点击任何阻挡 UI 元素。

const api = new CrawlingAPI({ token: 'YOUR_JS_TOKEN' });
const res = await api.get('https://spa.example.com', {
  page_wait: 2000,
  ajax_wait: true,
  scroll: true,
});

使用内置 scraper

在受支持的站点上完全跳过解析器。传入 scraper: 'NAME',响应 body 会变成一个 JSON 字符串,包含每个 scraper 页面所记录的结构化字段。

import { ScraperAPI } from 'crawlbase';

const api = new ScraperAPI({ token: 'YOUR_TOKEN' });
const res = await api.get(
  'https://www.amazon.com/dp/1098145356',
  { scraper: 'amazon-product-details' }
);
const data = JSON.parse(res.body);
console.log(data.name, data.price);

地理路由

传入 country: 'ISO' 通过该国家/地区的出口节点路由爬取请求。当目标站点根据 IP 提供本地化内容时使用。

const api = new CrawlingAPI({ token: 'YOUR_TOKEN' });

// Hit the German Amazon catalog from a German residential IP
const res = await api.get(
  'https://www.amazon.com/dp/1098145356',
  { country: 'DE' }
);

带退避的重试

推荐的重试形态:指数退避,限制在 3-5 次尝试以内,仅对临时性错误(5xx 或空 body)重试,对 4xx 不重试。

const api = new CrawlingAPI({ token: 'YOUR_TOKEN' });
const sleep = ms => new Promise(r => setTimeout(r, ms));

async function crawl(url, attempts = 5) {
  for (let i = 0; i < attempts; i++) {
    const res = await api.get(url);
    if (res.statusCode === 200 && Number(res.headers.pc_status) === 200) {
      return res;
    }
    if (res.statusCode >= 400 && res.statusCode < 500) {
      throw new Error(`client error ${res.statusCode}: ${url}`);
    }
    await sleep(Math.random() * (2 ** i) * 1000);
  }
  throw new Error(`Failed: ${url}`);
}

异步爬取 + webhook

fire-and-forget 模式。SDK 调用立即返回一个 rid;当页面准备好时,Crawlbase 会将结果 POST 到您的回调 URL。适用于批处理任务和慢速目标站点。

const api = new CrawlingAPI({ token: 'YOUR_TOKEN' });
const res = await api.get('https://example.com', {
  async: true,
  callback: 'https://your-app.com/webhook',
});
const rid = res.rid;  // correlate the eventual webhook delivery

// Your Express / Fastify / Hono webhook receives a POST with:
//   { rid, url, original_status, pc_status, body }

对于非常高的量(数百万 URL),请使用 Enterprise Crawler,它位于同一异步管道之前,具有重试、速率管理和结果交付功能。

粘性会话

某些流程需要在多次调用中保持相同的住宅 IP —— 如结账、分页搜索或登录会话。传入带有稳定标识符的 cookies_session,Crawlbase 会在约 30 分钟内复用同一出口节点。

const api = new CrawlingAPI({ token: 'YOUR_JS_TOKEN' });

const session = `checkout-${userId}`;
await api.get('https://shop.example.com/cart',     { cookies_session: session });
await api.get('https://shop.example.com/checkout', { cookies_session: session });
await api.get('https://shop.example.com/confirm',  { cookies_session: session });

错误与重试

Crawlbase 平台在每次响应中都会返回两个状态码:SDK 自身的 response.statusCode(向 Crawlbase 发起请求的 HTTP 状态码)和 pc_status 响应头(Crawlbase 对目标站点的判定结果 —— 完整列表见 Crawling API errors table)。Node SDK 将响应头作为普通对象暴露在 response.headers 上,因此判定结果读作 response.headers.pc_status。决定是否重试时请始终基于该字段进行分支判断 —— 目标站点可能返回 200 但 body 为空,此时 response.statusCode200,但 response.headers.pc_status520

const res = await api.get(url);
const pc = Number(res.headers.pc_status);

if (pc === 200) {
  use(res.body);
} else if (pc === 520 || pc === 525) {
  // 520 = empty body, 525 = anti-bot couldn't be solved.
  // Switch to JS token and retry.
  await retryWithJsToken(url);
} else if ([521, 522, 523].includes(pc)) {
  // Target unreachable or timed out. Retry with backoff.
  schedule_retry(url);
} else {
  logger.error('crawl failed', { url, pc_status: pc });
}

针对平台的所有重试都是免费的 —— 只有成功的响应(pc_status: 200)才计入您的配额。这让积极的退避策略几乎没有成本,重试唯一的真实代价只是增加的延迟。

性能与最佳实践

  • 每个 token 复用同一个客户端。 构造函数本身开销很小,但每个实例都会打开自己的底层连接池。在模块作用域内构建一次,并在所有调用之间共享。
  • 使用能完成任务的最便宜的 token。 不要 "以防万一" 就默认使用 JavaScript token —— Normal token 请求更快,占用的并发数也更少。只有当 Normal 响应为空或被反爬拦截时,才升级到 JS。
  • 优先使用 ajax_wait 而不是 page_wait 固定延迟会让每个请求都占用并发,即便是快速请求也不例外。
  • 批处理任务:async + webhook,或推送到 Enterprise Crawler。 同步模式是即兴和交互式使用的合适默认值;对于持续高吞吐量的提交,请切换到 async 模式,这样在请求入队的瞬间您的并发槽就会被释放。
  • 关注 remaining 响应头。 它记录了您剩余的并发槽位数 —— 健康的客户端会在触达上限之前主动退避。

方法参考

所有客户端类拥有相同的接口形态。构造函数接收一个 options 对象;方法名与底层 HTTP 方法一一对应。每个方法都返回一个 Promise。

new CrawlingAPI({ token, timeout })
constructor
使用您的 token 初始化客户端。可选参数:timeout,单位为毫秒(默认 90000)。
.get(url, options?)
method
发送 GET 请求。options 将任何 Crawling API 参数 映射到对应的值。
.post(url, data, options?)
method
发送一个 POST。data 是请求体 —— 传入对象表示表单编码,传入字符串表示原始内容。

响应结构(对象,所有属性都存在,即使为空):

response.statusCode
number
SDK 向 Crawlbase 发起请求的 HTTP 状态码。
response.body
string
页面内容(当使用 format=json / scraper= 时为 JSON 字符串)。默认按 UTF-8 解码。
response.url
string
目标站点重定向后的最终 URL。
response.headers
object
小写形式的响应 headers。Crawlbase 特有的状态字段在此处暴露:
  • response.headers.pc_status —— Crawlbase 对目标站点的判定结果(重试决策应基于此字段)。
  • response.headers.original_status —— 目标站点返回给 Crawlbase 的 HTTP 状态码。
  • response.headers.rid —— 请求 ID(当调用带有 async: truestore: true 时)。
response.json
object | undefined
当响应的 Content-Type 为 JSON 时,提供预先解析好的 JSON。SDK 已经解析过一次,您无需再做。