Node.js 中的 got-scraping 完整指南:头部生成与住宅代理实战

深入解析 Apify 的 got-scraping 库——从 header-generator 生成连贯浏览器头部、HTTP/2 支持,到通过 ProxyHat 住宅代理实现大规模 Node.js 爬虫。包含可运行代码、重试钩子、并发控制与生产模式最佳实践。

got-scraping in Node.js: A Developer's Guide with Residential Proxies
本文目录

免责声明:本文仅讨论公开可访问数据的采集。在美国,《计算机欺诈和滥用法》(CFAA)可能适用于未经授权的访问;在欧盟,GDPR 对个人数据的处理有严格规定。请始终遵守目标网站的 robots.txt、服务条款和适用法律,优先使用官方 API。

Node.js 中的 got-scraping:为什么你的原始请求会被拦截

如果你用过 gotaxios 做过规模化爬虫,大概率遇到过这种情况:前 100 个请求正常返回,然后突然全是 403 或 Cloudflare 验证码。原因不在你的代码逻辑,而在于请求的「指纹」暴露了自动化身份。

现代反爬系统不只看 IP 地址。它们检查至少三个层面:

  • HTTP 头部一致性 — 真实 Chrome 浏览器发送的头部有固定顺序,且包含 sec-ch-uasec-fetch-destaccept-language 等一系列客户端提示头部。got 默认发送的 user-agent 和少量头部立刻暴露非浏览器身份。
  • TLS 指纹(JA3/JA4) — Node.js 的 TLS 握手特征与 Chrome 不同,高级反爬系统可以通过 TLS 扩展顺序识别。
  • IP 信誉 — 数据中心 IP 段(AWS、GCP、DigitalOcean)被大量爬虫使用,反爬服务对这些段有标记。

got-scraping 是 Apify 开发的 got 扩展库,专门解决前两个问题。它内置 header-generator 模块,能生成与真实浏览器一致的头部集,并支持 HTTP/2。配合住宅代理解决 IP 信誉问题后,三层指纹都能被有效伪装。

header-generator:生成连贯的浏览器头部集

为什么头部顺序很重要

HTTP/1.1 规范并不强制头部顺序,但真实浏览器有固定的发送顺序。例如 Chrome 总是先发送 HostConnection,然后是 sec-ch-ua 系列,最后是 accept。反爬系统如果发现 user-agentaccept 之前,或者缺少 sec-fetch-mode,就会标记为可疑。

header-generator 不是简单地随机拼头部。它从真实浏览器采样数据,生成一个连贯的头部集——如果你指定 Chrome 120 on Windows,它会生成 Chrome 120 对应的所有客户端提示头部、正确的 accept 值、匹配的 sec-ch-ua-platform,且顺序与真实 Chrome 一致。

headerGeneratorOptions 配置

核心配置项包括:

  • browsers — 指定浏览器类型,如 ['chrome']['firefox']。建议只用 chrome,因为它是市场份额最大的浏览器,不容易引起怀疑。
  • devices'desktop''mobile'。移动端头部集与桌面端不同(如 sec-ch-ua-mobile 值)。
  • operatingSystems'windows''macos''linux''android''ios'。必须与浏览器和设备类型匹配。

一个常见错误是混合不兼容的组合,比如 chrome + ios(iOS 上的 Chrome 使用 WebKit 引擎,头部特征不同)。header-generator 内部有约束逻辑,但最好显式指定合理组合。

got-scraping 的惯用接口:got.extend 与 useHeaderGenerator

got-scraping 的设计遵循 got 的扩展模式。你不直接调用全局 got,而是通过 got.extend() 创建一个预配置实例。这是 got 推荐的做法——got 官方文档明确建议为不同用途创建独立实例。

基础实例创建

const { gotScraping } = require('got-scraping');

// 创建带 header-generator 的 got 实例
const client = gotScraping.extend({
  headerGeneratorOptions: {
    browsers: ['chrome'],
    devices: ['desktop'],
    operatingSystems: ['windows'],
  },
  http2: true, // 启用 HTTP/2,真实 Chrome 默认使用
});

// 每个请求自动获得连贯的浏览器头部
const response = await client.get('https://httpbin.org/headers');
console.log(JSON.parse(response.body).headers);

注意 http2: true。现代浏览器默认使用 HTTP/2 连接目标网站。如果你的爬虫用 HTTP/1.1 请求一个支持 HTTP/2 的网站,这本身就是一个异常信号。got-scraping 基于 got 的 HTTP/2 实现,能正确处理 ALPN 协商。

proxyUrl 选项:HTTP 与 SOCKS5

got-scraping 通过 proxyUrl 选项支持代理。可以在实例级别设置,也可以在单个请求级别覆盖:

// 实例级别:所有请求走同一代理
const client = gotScraping.extend({
  headerGeneratorOptions: {
    browsers: ['chrome'],
    devices: ['desktop'],
    operatingSystems: ['windows'],
  },
  proxyUrl: 'http://user-country-US:pass@gate.proxyhat.com:8080',
});

// 请求级别:覆盖代理(例如切换国家)
const resp = await client.get('https://httpbin.org/ip', {
  proxyUrl: 'http://user-country-DE:pass@gate.proxyhat.com:8080',
});

对于 SOCKS5 代理,使用端口 1080

proxyUrl: 'socks5://user-country-US:pass@gate.proxyhat.com:1080'

SOCKS5 在某些场景下比 HTTP 代理更可靠,因为它不修改 HTTP 头部,且支持 UDP。但 HTTP 代理在大多数爬虫场景中已经足够,且调试更方便。

通过 ProxyHat 住宅代理路由请求

为什么头部对了还需要住宅 IP

假设你已经用 got-scraping 生成了完美的 Chrome 头部,TLS 也通过 HTTP/2 对齐了。但如果你从一个 AWS us-east-1 的 IP 发送请求,目标网站的反爬服务(如 Cloudflare、DataDome、PerimeterX)仍然会标记你——因为真实用户不会从数据中心 IP 访问电商网站。

住宅代理的 IP 来自真实 ISP 分配给家庭用户的地址段。当请求从这些 IP 发出时,反爬系统看到的是一个「普通家庭网络用户」,而不是「服务器上的爬虫脚本」。这就是为什么头部伪装和 IP 伪装必须同时做——只做一层不够。

ProxyHat 的住宅代理网关位于 gate.proxyhat.com:8080(HTTP)和 gate.proxyhat.com:1080(SOCKS5)。你可以在用户名中嵌入地理定位和会话标识:

  • user-country-US — 美国出口 IP
  • user-country-DE-city-berlin — 德国柏林出口 IP
  • user-session-abc123 — 粘性会话,同一 session ID 保持同一 IP

查看可用地理位置请访问 ProxyHat 代理位置页面

住宅 vs 数据中心 vs 移动代理对比

类型 IP 来源 信任度 速度 成本 适用场景
住宅代理 真实 ISP 家庭网络 中等(50-200ms) 中等 SERP 爬取、电商价格监控、社交媒体调研
数据中心代理 云服务商 IP 段 快(10-50ms) 无反爬的 API 调用、低风险页面
移动代理 4G/5G 运营商网络 极高 较慢(100-500ms) 社交账号管理、高信任度要求场景

可运行示例:轮换住宅端点与重试钩子

下面是一个完整的 Node.js 脚本,展示如何用 got-scraping 配合 ProxyHat 住宅代理实现带重试的轮换爬取。每个请求生成唯一的 session ID,确保 IP 轮换。

const { gotScraping } = require('got-scraping');
const crypto = require('crypto');

// ProxyHat 凭据
const PROXYHAT_USER = 'your_username';
const PROXYHAT_PASS = 'your_password';
const GATEWAY = 'gate.proxyhat.com';
const HTTP_PORT = 8080;

// 生成带 session 和 geo 的代理 URL
function buildProxyUrl({ country = 'US', session } = {}) {
  const sid = session || crypto.randomUUID().substring(0, 8);
  const username = `${PROXYHAT_USER}-country-${country}-session-${sid}`;
  return `http://${username}:${PROXYHAT_PASS}@${GATEWAY}:${HTTP_PORT}`;
}

// 创建 got-scraping 实例,带重试钩子
const client = gotScraping.extend({
  headerGeneratorOptions: {
    browsers: ['chrome'],
    devices: ['desktop'],
    operatingSystems: ['windows'],
  },
  http2: true,
  retry: {
    limit: 3,
    statusCodes: [429, 503, 502, 504],
    backoffLimit: 5000,
  },
  hooks: {
    beforeRequest: [
      (options) => {
        // 每个请求自动生成新 session → 新 IP
        if (!options.proxyUrl || !options.context?.sticky) {
          options.proxyUrl = buildProxyUrl({
            country: options.context?.country || 'US',
          });
        }
      },
    ],
    afterResponse: [
      (response, retryWithMergedOptions) => {
        // 检测 CAPTCHA 页面
        const body = response.body;
        if (typeof body === 'string' && body.includes('cf-challenge')) {
            // 换一个新 session 重试
            retryWithMergedOptions({
              proxyUrl: buildProxyUrl({ country: 'US' }),
            });
        }
        return response;
      },
    ],
  },
});

// 批量请求示例
async function scrapeUrls(urls) {
  const results = [];
  for (const url of urls) {
    try {
      const resp = await client.get(url, {
        context: { country: 'US' },
      });
      results.push({ url, status: resp.statusCode, ok: true });
    } catch (err) {
      results.push({ url, status: err.response?.statusCode, ok: false });
    }
  }
  return results;
}

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

scrapeUrls(targets).then(console.log);

关键设计点:

  • beforeRequest 钩子在每个请求前生成新的 proxyUrl,通过随机 session ID 实现自动 IP 轮换。
  • retry 配置对 429 和 503 自动重试,最多 3 次,退避上限 5 秒。
  • afterResponse 钩子检测 Cloudflare challenge 页面,触发带新 IP 的重试。
  • context 对象用于在请求间传递自定义参数(如国家代码),不污染 HTTP 头部。

用 p-limit 控制并发

无限制并发是爬虫被封锁的头号原因。即使每个请求都有不同的 IP,同一秒发出 500 个请求到同一域名也会触发行为分析。使用 p-limit 限制并发:

const pLimit = require('p-limit');

const limit = pLimit(10); // 最多 10 个并发请求

async function scrapeWithConcurrency(urls) {
  const tasks = urls.map(url =>
    limit(() => client.get(url).then(
      r => ({ url, ok: true, status: r.statusCode }),
      e => ({ url, ok: false, status: e.response?.statusCode })
    ))
  );
  return Promise.all(tasks);
}

经验值:对大多数网站,5-20 个并发是安全区间。SERP 爬取建议更低(3-5),因为搜索引擎对自动化访问特别敏感。更多 SERP 爬取策略参见 ProxyHat SERP 跟踪用例

某些场景需要多个请求共享 Cookie(如登录后的分页爬取)。got 内置 tough-cookie 支持:

const { gotScraping } = require('got-scraping');
const { CookieJar } = require('tough-cookie');

const cookieJar = new CookieJar();

const sessionClient = gotScraping.extend({
  headerGeneratorOptions: {
    browsers: ['chrome'],
    devices: ['desktop'],
    operatingSystems: ['windows'],
  },
  cookieJar,
  proxyUrl: buildProxyUrl({
    country: 'US',
    session: 'login-flow-001', // sticky session 保持同一 IP
  }),
});

// 第一步:获取 CSRF token
const page1 = await sessionClient.get('https://example.com/login');
// 第二步:提交登录(复用 Cookie 和 IP)
const page2 = await sessionClient.post('https://example.com/login', {
  form: { username: '...', password: '...' },
});
// 第三步:访问受保护页面
const page3 = await sessionClient.get('https://example.com/dashboard');

这里 session: 'login-flow-001' 确保三个请求通过同一住宅 IP 发出。如果每个请求换 IP,目标网站会因 IP 跳变而中断会话。

升级到 Crawlee 的 CheerioCrawler

当爬虫规模增长到需要 URL 队列、自动重试、去重和结果存储时,直接用 got-scraping 管理会变得复杂。Apify 的 Crawlee 框架内置了 got-scraping,通过 CheerioCrawler 提供 HTML 解析和爬取编排:

const { CheerioCrawler } = require('crawlee');

const crawler = new CheerioCrawler({
  requestHandler: async ({ $, request }) => {
    const title = $('title').text();
    const links = $('a[href]').map((_, el) => $(el).attr('href')).get();
    console.log(`${request.url}: ${title} (${links.length} links)`);
  },
  // got-scraping 配置透传
  proxyConfiguration: {
    newUrlFunction: (sessionId) =>
      buildProxyUrl({ country: 'US', session: sessionId }),
  },
  maxConcurrency: 10,
  maxRequestRetries: 3,
});

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

CheerioCrawler 自动使用 got-scraping 发送请求,你只需关注解析逻辑。代理轮换通过 proxyConfigurationnewUrlFunction 实现,每个请求获得独立的 session ID。

何时必须用无头浏览器

got-scraping 能解决头部和 HTTP 协议层的伪装,但无法执行 JavaScript。以下场景必须升级到 Playwright 或 Puppeteer:

  • 客户端渲染的 SPA — 页面内容通过 JS 动态加载,HTTP 请求只返回骨架 HTML。
  • Cloudflare Turnstile / reCAPTCHA — 需要真实浏览器环境执行挑战脚本。即使如此,也应配合住宅代理使用。
  • 需要交互的流程 — 点击、滚动、等待动态元素加载。

无头浏览器的成本是 10-50 倍的内存和 CPU 开销。一个 Chrome 实例约占 150-300 MB 内存,而 got-scraping 请求只需约 2 MB。在容器化部署中,10 个 Chrome 实例需要约 3 GB 内存,而 100 个 got-scraping 并发连接只需约 200 MB。

最佳策略是分层爬取:先用 got-scraping 爬取能直接获取 HTML 的页面,只对需要 JS 渲染的页面降级到无头浏览器。Crawlee 的 PlaywrightCrawlerCheerioCrawler 可以在同一个项目中混用。

伦理与合规要点

技术能力不等于合法使用权。在开始任何爬虫项目前:

  • 检查 robots.txt — 虽然法律效力存在争议,但它是网站表达意愿的明确信号。
  • 遵守服务条款 — 许多网站的 ToS 明确禁止自动化访问,违反可能导致账号封禁或法律行动。
  • 控制请求频率 — 即使没有明确的 rate limit,也应保持合理间隔(如每秒不超过 1-2 请求/域名)。
  • 优先使用官方 API — 如果目标网站提供 API,用 API 而非爬虫。API 更稳定、更合法、更不易被封。
  • 不采集个人数据 — GDPR 定义的个人数据(姓名、邮箱、IP、位置等)的处理需要法律依据。

更多爬虫最佳实践参见 ProxyHat 网页爬虫用例ProxyHat 官方文档

关键要点

got-scraping 的价值在于「连贯性」 — 它不是简单地加一个 User-Agent,而是生成与真实浏览器完全一致的头部集(顺序、客户端提示、accept 值),配合 HTTP/2 对齐 TLS 层。这是它区别于手动设置头部的核心优势。

  • 三层伪装缺一不可 — 头部(header-generator)+ 协议(HTTP/2)+ IP(住宅代理)。只做一层会被多层检测系统识破。
  • got.extend 是惯用法 — 创建预配置实例,通过 hooks 注入代理轮换和重试逻辑,而非在每个请求中重复配置。
  • session ID 实现 IP 控制 — 随机 session = 自动轮换;固定 session = 粘性会话。ProxyHat 的用户名参数化设计让这变得简单。
  • 并发控制是生产底线 — 用 p-limit 或 Crawlee 的 maxConcurrency 限制并发,5-20 是安全区间。
  • 无头浏览器是最后手段 — 只在需要 JS 渲染或交互式挑战时使用,资源开销是 got-scraping 的 10-50 倍。
  • 合规优先 — robots.txt、ToS、GDPR/CFAA 不是可选项。优先用官方 API。

准备好开始了吗?查看 ProxyHat 定价方案,选择适合你规模的住宅代理套餐,然后复制上面的代码示例即可运行。

常见问题

什么是 Node.js 中的 got-scraping?

got-scraping 是 Apify 基于 got 构建的 HTTP 客户端扩展,专为爬虫场景设计。它内置 header-generator 模块,能自动生成与真实浏览器一致的 HTTP 请求头(包括头部顺序、sec-ch-ua、accept-language 等),并支持 HTTP/2。通过 got.extend 创建自定义实例,开发者可以集成代理、重试逻辑和 Cookie 管理,无需手动拼接头部。

为什么 got-scraping 对代理用户很重要?

即使使用了住宅代理,如果请求头部与真实浏览器不匹配,反爬系统仍能通过 TLS 指纹和头部顺序识别自动化流量。got-scraping 的 header-generator 确保每个请求携带连贯的浏览器头部集,配合住宅 IP 使得请求在头部层和网络层都看起来像真实用户访问,显著降低被封概率。

got-scraping 在 Node.js 中最适合哪种代理类型?

住宅代理是最佳选择,因为其 IP 来自真实 ISP 分配的家用网络,与浏览器头部配合时几乎无法被区分。数据中心代理虽然速度快,但 IP 段容易被批量封禁。移动代理适合需要极高信任度的场景但成本较高。got-scraping 通过 proxyUrl 选项支持 HTTP/HTTPS 和 SOCKS5 代理,可灵活切换。

如何在 got-scraping 中避免被封锁?

关键策略包括:使用 header-generator 生成真实浏览器头部、通过住宅代理轮换 IP、设置合理的请求间隔避免高频访问、使用 sticky session 在多步流程中保持同一 IP、实现重试钩子处理 429/503 状态码、限制并发数量避免触发速率限制,以及遵守目标网站的 robots.txt 和服务条款。

准备好开始了吗?

覆盖 148+ 国家的住宅、ISP 和移动代理。创建免费账户。

创建免费账户
← 返回博客