got-scraping Node.js 完全指南:浏览器级请求头与住宅代理集成

深入讲解 Apify 的 got-scraping 库在 Node.js 中的用法,涵盖 HeaderGenerator 生成连贯浏览器请求头、HTTP/2 支持、住宅代理路由、重试钩子与生产级并发模式。

got-scraping in Node.js: A Developer's Guide to Headers, HTTP/2 & Residential Proxies

为什么 Node.js 中的 got-scraping 比原生 got 更适合规模化抓取

如果你在 Node.js 中用 gotaxios 发过请求,大概率遇到过这种情况:代码逻辑完全正确,但目标站点返回 403 或 429,甚至直接 TCP 重置。原因不在于你的爬虫写得不好,而在于原生 HTTP 客户端发出的请求指纹和真实浏览器差异巨大——请求头顺序错乱、缺失 sec-ch-ua 系列标头、TLS JA3 指纹暴露非浏览器身份。got-scraping 是 Apify 团队基于 got 封装的 HTTP 客户端,专门解决这些问题。在 Node.js 中的 got-scraping 能自动生成连贯的浏览器请求头集合,支持 HTTP/2,并与代理无缝集成。

对于需要 got-scraping proxy 方案的开发者来说,核心价值在于:你不再需要手动维护 User-Agent 与 Accept-Language 的搭配,也不用担心 sec-fetch-* 标头的顺序——库内部通过 HeaderGenerator 生成一组逻辑自洽的浏览器指纹,从 Chrome 版本号到操作系统再到设备类型全部匹配。

技术背景:为什么原生请求会被标记

现代反爬系统(如 Cloudflare、Datadome、PerimeterX)不再只看单个请求头,而是分析请求头集合的一致性。以下是原生 got 请求被标记的三个主要原因:

1. 请求头顺序与缺失

真实 Chrome 浏览器发送的请求头有固定顺序,且包含 sec-ch-uasec-ch-ua-mobilesec-ch-ua-platform 等客户端提示标头。原生 got 默认不发送这些标头,或者开发者手动添加后顺序与真实浏览器不一致。根据 MDN 文档sec-ch-ua 是 User-Agent Client Hints 的一部分,现代反爬系统会检查其存在性和与 UA 字符串的一致性。

2. TLS 指纹差异

Node.js 的 TLS 栈与 Chrome 的 BoringSSL 在 ClientHello 阶段就不同——扩展顺序、密码套件列表、ALPN 协商结果都有差异。这产生所谓的 JA3/JA4 指纹。got-scraping 虽不能改变底层 TLS 栈,但通过 HTTP/2 支持和完整的浏览器标头集合,能显著降低被标记概率。

3. IP 信誉

即使请求头完美,如果 IP 来自已知数据中心段(AWS、GCP、Azure),很多站点会直接拒绝。这就是为什么 nodejs scraping proxy 方案中住宅代理是刚需——住宅 IP 来自真实 ISP,信誉远高于数据中心 IP。

got-scraping 的惯用接口

got-scraping 的设计哲学是通过 got.extend 组合中间件,而非 hack 式覆盖。核心 API 表面包括:

got.extend 与 useHeaderGenerator

got-scraping 导出一个已经过扩展的 got 实例,内置了 useHeaderGenerator 钩子。你也可以进一步自定义:

import { gotScraping } from 'got-scraping';
import { HeaderGenerator } from 'header-generator';

const generator = new HeaderGenerator({
  browsers: ['chrome'],
  devices: ['desktop'],
  operatingSystems: ['linux', 'macos'],
  locales: ['en-US', 'de-DE'],
});

const client = gotScraping.extend({
  headerGeneratorOptions: {
    browsers: ['chrome'],
    devices: ['desktop'],
    operatingSystems: ['macos'],
  },
  // 每次请求自动生成连贯的浏览器标头
  hooks: {
    beforeRequest: [
      (options) => {
        const headers = generator.getHeaders();
        options.headers = { ...headers, ...options.headers };
      },
    ],
  },
});

const res = await client('https://example.com');
console.log(res.statusCode); // 200

headerGeneratorOptions 接受 browserschromefirefoxsafari)、devicesdesktopmobile)、operatingSystemswindowsmacoslinuxandroidios)等参数。生成器会确保 UA 字符串、sec-ch-ua-platformAccept-Language 之间逻辑一致——例如 macOS + Chrome 组合不会出现 Windows NT 字样。

proxyUrl 选项与 HTTP/2 代理

got-scraping 支持 proxyUrl 选项,兼容 HTTP/1.1 和 HTTP/2 代理。这是 got scraping header generator 之外的另一个关键能力——你可以在保持浏览器级标头的同时,将流量路由到住宅代理:

import { gotScraping } from 'got-scraping';

const res = await gotScraping({
  url: 'https://httpbin.org/headers',
  proxyUrl: 'http://user-country-US:pass@gate.proxyhat.com:8080',
  headerGeneratorOptions: {
    browsers: ['chrome'],
    devices: ['desktop'],
    operatingSystems: ['windows'],
  },
});
console.log(JSON.parse(res.body).headers['user-agent']);
// Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...

通过 ProxyHat 住宅代理路由流量

当请求头和 TLS 看起来像真实浏览器后,IP 信誉成为最后一道门槛。数据中心 IP 即便请求头完美,仍可能被 Cloudflare 的 IP 信誉库标记。住宅代理通过真实 ISP 分配的 IP 地址发送请求,目标站点看到的流量来源与普通用户无异。

ProxyHat 的住宅代理网关地址为 gate.proxyhat.com,HTTP 默认端口 8080,SOCKS5 端口 1080。用户名中可嵌入地理定位和会话保持参数:

  • user-country-US:pass@gate.proxyhat.com:8080 — 美国出口 IP
  • user-country-DE-city-berlin:pass@gate.proxyhat.com:8080 — 柏林城市级定位
  • user-session-abc123:pass@gate.proxyhat.com:8080 — 粘性会话,同一 session ID 保持同一 IP

SOCKS5 代理同样支持,只需将端口改为 1080

const res = await gotScraping({
  url: 'https://httpbin.org/ip',
  proxyUrl: 'socks5://user-country-DE:pass@gate.proxyhat.com:1080',
  headerGeneratorOptions: {
    browsers: ['firefox'],
    devices: ['desktop'],
    operatingSystems: ['linux'],
  },
});

SOCKS5 在某些场景下比 HTTP 代理更灵活——它不修改请求头,支持任意 TCP 流量。但对于纯 HTTP/HTTPS 抓取,8080 端口的 HTTP 代理通常延迟更低、配置更简单。你可以在 ProxyHat 位置页面 查看支持的地理定位选项。

完整示例:轮换住宅端点 + 重试钩子

下面是一个可运行的 Node.js 示例,展示如何用 got-scraping 配合 ProxyHat 住宅代理实现每请求轮换、自动重试和会话保持:

import { gotScraping } from 'got-scraping';
import pLimit from 'p-limit';

// ProxyHat 用户名生成器:每请求生成唯一 session ID
function buildProxyUrl(country, sessionId) {
  const user = `user-country-${country}-session-${sessionId}`;
  const pass = process.env.PROXYHAT_PASS;
  return `http://${user}:${pass}@gate.proxyhat.com:8080`;
}

// 扩展 got-scraping 客户端,内置重试和标头生成
const client = gotScraping.extend({
  retry: {
    limit: 3,
    statusCodes: [403, 429, 503],
  },
  hooks: {
    beforeRequest: [
      (options) => {
        // 为每次请求生成随机 session ID 实现轮换
        const sessionId = Math.random().toString(36).slice(2, 10);
        options.proxyUrl = buildProxyUrl('US', sessionId);
      },
    ],
    afterResponse: [
      (response, retryWithMergedOptions) => {
        if (response.statusCode === 403) {
          // 403 时切换代理并重试
          return retryWithMergedOptions({
            proxyUrl: buildProxyUrl('US', Date.now().toString()),
          });
        }
        return response;
      },
    ],
  },
  headerGeneratorOptions: {
    browsers: ['chrome'],
    devices: ['desktop'],
    operatingSystems: ['windows', 'macos'],
  },
});

// 限制并发为 10,避免触发目标站点速率限制
const limit = pLimit(10);

const urls = [
  'https://httpbin.org/ip',
  'https://httpbin.org/headers',
  'https://httpbin.org/user-agent',
];

const results = await Promise.all(
  urls.map((url) =>
    limit(() => client(url).then((r) => r.body).catch((e) => e.message))
  )
);

console.log(results);

这个示例展示了几个关键模式:retry 配置自动重试 403/429/503;beforeRequest 钩子在每请求级别注入代理 URL;afterResponse 钩子在 403 时主动切换代理;p-limit 限制并发到 10 个请求。对于更大规模的任务,建议查看 ProxyHat 定价页了解可用带宽和并发限制。

生产模式:重试钩子、并发控制与 Cookie Jar

重试与 afterResponse 钩子

got-scraping 继承了 got 的完整钩子系统。beforeRetry 钩子在每次重试前触发,适合用于切换代理或刷新标头:

const client = gotScraping.extend({
  retry: { limit: 4, backoffLimit: 5000 },
  hooks: {
    beforeRetry: [
      (options, error, retryCount) => {
        console.log(`Retry #${retryCount} for ${options.url}`);
        // 每次重试换一个 session ID
        const newSession = `retry-${retryCount}-${Date.now()}`;
        options.proxyUrl = buildProxyUrl('US', newSession);
        // 重新生成标头
        options.headers = new HeaderGenerator({
          browsers: ['chrome'],
          devices: ['desktop'],
          operatingSystems: ['windows'],
        }).getHeaders();
      },
    ],
  },
});

有界并发与 p-limit

无限制的 Promise.all 会在目标站点触发速率限制,也会耗尽本地端口。使用 p-limit 将并发控制在合理范围(通常 5–20 个并发请求)是生产环境的基本做法。对于 SERP 跟踪等场景,建议并发不超过 5,并在请求间加入 1–2 秒的随机延迟。

Cookie Jar 与会话保持

某些站点需要多步流程(登录、分页),此时需要跨请求保持 Cookie。got-scraping 通过 tough-cookie 支持 Cookie Jar:

import { gotScraping } from 'got-scraping';
import { CookieJar } from 'tough-cookie';

const jar = new CookieJar();
const sessionClient = gotScraping.extend({
  cookieJar: jar,
  proxyUrl: 'http://user-country-US-session-sticky01:pass@gate.proxyhat.com:8080',
  headerGeneratorOptions: {
    browsers: ['chrome'],
    devices: ['desktop'],
    operatingSystems: ['macos'],
  },
});

// 第一步:获取 CSRF token
await sessionClient('https://example.com/login');
// 第二步:提交表单(Cookie 自动携带)
await sessionClient.post('https://example.com/login', {
  form: { username: 'foo', password: 'bar' },
});

注意这里使用了 session-sticky01 固定会话 ID,确保整个流程中 IP 不变。Cookie Jar 和粘性会话的组合是多步抓取的基础。

升级到 Crawlee CheerioCrawler

当你的抓取需求从几十个 URL 增长到数千个时,手动管理队列、重试和去重变得繁琐。Crawlee 是 Apify 的爬虫框架,内置了 CheerioCrawler,底层正是 got-scraping:

import { CheerioCrawler } from 'crawlee';

const crawler = new CheerioCrawler({
  requestHandler: async ({ $, request }) => {
    const title = $('title').text();
    console.log(`${request.url} → ${title}`);
  },
  proxyConfiguration: {
    newUrlFunction: (sessionId) =>
      `http://user-country-US-session-${sessionId}:pass@gate.proxyhat.com:8080`,
  },
  maxConcurrency: 10,
  maxRequestRetries: 3,
});

await crawler.run(['https://example.com/page1', 'https://example.com/page2']);

CheerioCrawler 自动处理请求队列、去重、重试和错误日志,同时复用 got-scraping 的标头生成和代理支持。这是从脚本到生产级爬虫的自然升级路径。更多抓取场景可参考 ProxyHat 网页抓取用例

何时需要无头浏览器

got-scraping 能解决大部分静态内容和 API 端点的抓取需求,但当目标站点满足以下条件时,无头浏览器(Playwright/Puppeteer)不可避免:

  • 页面内容由 JavaScript 动态渲染,且没有可逆向的 XHR 端点
  • 站点使用 Cloudflare Turnstile 或 reCAPTCHA 等挑战,需要真实浏览器环境求解
  • 需要模拟复杂交互(悬停、拖拽、Canvas 指纹)

无头浏览器的资源消耗是 got-scraping 的 10–50 倍——一个 Chrome 实例占用约 200–300 MB 内存,而 got-scraping 请求仅需几 MB。因此,优先尝试 HTTP 抓取,仅在必要时降级到无头浏览器。Crawlee 的 PlaywrightCrawler 同样支持 ProxyHat 代理配置,可以无缝切换。

对于 SERP 跟踪场景,大多数搜索引擎提供结构化结果页面,got-scraping 配合住宅代理通常足够。详见 SERP 跟踪用例

伦理与法律注意事项

在开始任何抓取项目之前,请确认:

  • 仅抓取公开可访问的数据,不绕过认证或付费墙
  • 遵守目标站点的 robots.txt 和服务条款
  • 尊重速率限制,设置合理的请求间隔
  • 优先使用官方 API——如果目标平台提供 API,抓取 HTML 通常是不必要且不稳定的

在美国,计算机欺诈和滥用法案(CFAA)可能适用于未经授权的访问。在欧盟,GDPR 要求数据控制者对个人数据的处理承担责任——即使数据是公开抓取的,存储和分析包含个人信息的页面仍需合规。这不是法律建议,请在具体项目中咨询专业律师。

更多代理配置细节可参考 ProxyHat 官方文档

关键要点

  • got-scraping 的核心价值:通过 HeaderGenerator 自动生成连贯的浏览器标头集合,消除手动维护 UA/Accept/sec-ch-ua 一致性的负担。
  • 住宅代理是必需品而非可选:当标头和 TLS 看起来像真实浏览器后,IP 信誉成为决定性因素。数据中心 IP 在很多站点会被直接拒绝。
  • 惯用模式:用 got.extend 组合 headerGeneratorOptionsproxyUrl 和钩子,而非 hack 式覆盖。
  • 生产模式p-limit 控制并发、beforeRetry 切换代理、tough-cookie 保持会话,大规模时升级到 Crawlee。
  • 降级策略:优先 HTTP 抓取,仅在动态渲染或挑战页面时使用无头浏览器。

准备开始了吗?

通过AI过滤访问148多个国家的5000多万个住宅IP。

查看价格住宅代理
← 返回博客