Node.jsのgot-scrapingとは何か、なぜ重要なのか
Node.jsのgot-scrapingは、Apifyが開発したgotベースのHTTPクライアントで、スクレイピングに特化したヘッダー生成とプロキシサポートを組み込んでいます。素のgotやaxiosでは、ブラウザらしいヘッダー順序やSec-CH-UA、Accept-Languageの整合性を手動で構築する必要がありますが、got-scrapingはこれを自動化します。本記事では、got-scraping proxyの設定からresidential proxyのローテーションまで、実行可能なコード例とともに解説します。
Webスクレイピングで最も多い失敗パターンは「リクエストは送信できるが、HTTP 403やCAPTCHAページが返ってくる」ことです。原因の多くはヘッダーの不整合とIPの信頼性不足にあります。got-scrapingは前者を解決し、ProxyHatのresidential proxyは後者を解決します。両方を組み合わせることで、Webスクレイピングの成功率を大幅に向上させることができます。
注意: 本記事で扱うスクレイピングは公開データの収集に限定します。米国のCFAA(Computer Fraud and Abuse Act)や欧州のGDPRは、アクセス制限のあるデータへのスクレイピングに民事・刑事責任を科す可能性があります。常にrobots.txtを確認し、利用規約を尊重し、公式APIが利用可能な場合はそちらを優先してください。
素のgot/axiosがブロックされる技術的理由
現代のWebサイトは、リクエストが本物のブラウザから来ているかを複数のシグナルで判定しています。主なチェックポイントは以下の通りです:
- ヘッダー順序: ブラウザは特定の順序でヘッダーを送信します。例えばChromeは
sec-ch-uaをacceptの前に送りますが、Node.jsのHTTPクライアントは異なる順序を使います。MDNのドキュメントで確認できる通り、Sec-CH-UAヘッダーはClient Hints仕様の一部であり、正しい値と順序が必要です。 - TLS指紋: Node.jsのTLSスタックはブラウザと異なる暗号スイートの優先順位を持つため、JA3/JA4フィンガープリントで識別される可能性があります。
- HTTP/2の欠如: モダンブラウザはHTTP/2でリクエストを送信しますが、axiosや素のgotはデフォルトでHTTP/1.1を使用します。HTTP/2ストリームの存在自体がブラウザらしさのシグナルになります。
- IPの信頼性: データセンターIPはAWS、GCP、AzureなどのASNに属するため、プロキシとして使用されていることが一目で分かります。CloudflareやAkamaiのようなWAFは、データセンターIPからのリクエストを自動的にスコアリングします。
これらのシグナルのうち1つでも欠けていると、WAFはリクエストを「ボット」と判定してブロックします。got-scrapingはヘッダーとHTTP/2の問題を解決し、residential proxyはIPの問題を解決します。
| 機能 | got-scraping | 素のgot | axios | Playwright |
|---|---|---|---|---|
| ヘッダー生成 | 自動(header-generator) | 手動 | 手動 | ブラウザ内蔵 |
| HTTP/2サポート | あり | なし(デフォルト) | なし | あり |
| プロキシサポート | proxyUrl(HTTP/SOCKS5) | 手動設定 | 手動設定 | 組み込み |
| TLS指紋 | Node.jsベース | Node.jsベース | Node.jsベース | ブラウザと同一 |
| パフォーマンス | 高速(約10ms/リクエスト) | 高速 | 高速 | 低速(500ms以上) |
| 適合規模 | 10,000+ req/h | 10,000+ req/h | 10,000+ req/h | 1,000 req/h程度 |
got-scrapingのAPIサーフェスとヘッダー生成
got-scrapingはgotの拡張として実装されており、gotの全機能(フック、リトライ、ストリーム)をそのまま利用できます。重要な拡張ポイントは3つあります:got.extendによるクライアント構築、useHeaderGeneratorとheaderGeneratorOptionsによるヘッダー制御、そしてproxyUrlオプションによるプロキシルーティングです。
got.extendによるクライアント構築
gotScraping.extend()を使うと、デフォルトオプションを設定した再利用可能なクライアントを作成できます。これにより、各リクエストでヘッダー生成オプションやプロキシURLを繰り返し指定する必要がなくなります。
import { gotScraping } from 'got-scraping';
const client = gotScraping.extend({
useHeaderGenerator: true,
headerGeneratorOptions: {
browsers: ['chrome'],
devices: ['desktop'],
operatingSystems: ['windows', 'macos'],
},
retry: { limit: 3, statusCodes: [403, 429, 500, 502, 503] },
timeout: { request: 15000 },
});
const response = await client('https://example.com');
console.log(response.statusCode); // 200
useHeaderGeneratorとheaderGeneratorOptions
useHeaderGeneratorはデフォルトでtrueです。headerGeneratorOptionsでブラウザ、デバイス、OSを指定すると、header-generatorライブラリがそれらに整合するヘッダーセットを生成します。例えばbrowsers: ['chrome']を指定すると、Chromeが送信する正しい順序のSec-CH-UA、Accept、Accept-Language、Accept-Encodingヘッダーが生成されます。got scraping header generatorは、ヘッダーの値だけでなく送信順序も再現するため、WAFのヘッダー順序チェックを通過できます。
const client = gotScraping.extend({
headerGeneratorOptions: {
browsers: ['chrome', 'firefox'],
devices: ['desktop', 'mobile'],
operatingSystems: ['windows', 'macos', 'android', 'ios'],
},
});
複数のブラウザやOSを指定すると、リクエストごとにランダムに選択されます。これにより、単一のヘッダーパターンに固執せず、より自然なトラフィックパターンを作り出せます。
proxyUrlオプション
got-scrapingはproxyUrlオプションでHTTP/HTTPSプロキシとSOCKS5プロキシの両方をサポートしています。HTTP/2プロキシもサポートされており、エンドツーエンドでHTTP/2通信を行うことができます。nodejs scraping proxyの設定は、このproxyUrl1つで完結します。
const response = await gotScraping({
url: 'https://example.com',
proxyUrl: 'http://user-country-US-session-abc123:pass@gate.proxyhat.com:8080',
});
residential proxyのルーティング
ヘッダーとTLSがブラウザらしく見えても、リクエスト元のIPがデータセンターであれば、WAFはそれをフラグ付けします。ProxyHatのresidential proxyは、実際のISPに登録された住宅IPアドレスを使用するため、トラフィックが本物のユーザーから来ているように見えます。
ProxyHatのゲートウェイはgate.proxyhat.comで、HTTPプロキシはポート8080、SOCKS5プロキシはポート1080でリッスンしています。ユーザー名に国、都市、セッションIDを指定することで、ジオターゲティングとスティッキーセッションを制御できます:
user-country-US:pass— 米国のIPを使用user-country-DE-city-berlin:pass— ベルリンのIPを使用user-country-US-session-abc123:pass— 同一セッションで同じIPを維持
セッションIDはスティッキーセッションを実現します。同じセッションIDを使い続ける限り、同じIPアドレスが割り当てられます。これにより、ログイン後のセッション維持や、IPベースのレート制限を回避しながら一貫したアクセスが可能になります。
SOCKS5プロキシの使用
一部の環境ではHTTPプロキシがブロックされている場合があります。その場合はSOCKS5プロキシをポート1080で使用できます。got-scrapingはSOCKS5をネイティブサポートしているため、追加のパッケージは不要です:
const response = await gotScraping({
url: 'https://example.com',
proxyUrl: 'socks5://user-country-US-session-abc123:pass@gate.proxyhat.com:1080',
});
実践例:国別・セッション別ローテーション
以下は、ProxyHatのresidential proxyを使ってリクエストごとに異なるセッションを生成し、リトライフックを組み合わせた実践的な例です。beforeRetryフックでリトライ時に新しいセッションIDを生成し、自動的にIPを切り替えています:
import { gotScraping } from 'got-scraping';
import { randomUUID } from 'crypto';
// ProxyHatのユーザー名を生成するヘルパー
function buildProxyUsername({ country, session, city }) {
let username = 'user';
if (country) username += `-country-${country}`;
if (city) username += `-city-${city}`;
if (session) username += `-session-${session}`;
return username;
}
// プロキシURLを生成
function buildProxyUrl({ country, session, city, protocol = 'http' }) {
const port = protocol === 'socks5' ? 1080 : 8080;
const username = buildProxyUsername({ country, session, city });
return `${protocol}://${username}:YOUR_PASSWORD@gate.proxyhat.com:${port}`;
}
// 拡張クライアントの構築
const client = gotScraping.extend({
useHeaderGenerator: true,
headerGeneratorOptions: {
browsers: ['chrome'],
devices: ['desktop'],
operatingSystems: ['windows', 'macos'],
},
retry: {
limit: 3,
statusCodes: [403, 429, 500, 502, 503],
backoffLimit: 5000,
},
hooks: {
beforeRetry: [
(error, retryWithMergedOptions) => {
// リトライ時に新しいセッションIDを生成してIPを切り替え
const newSession = randomUUID().slice(0, 8);
const newProxyUrl = buildProxyUrl({
country: 'US',
session: newSession,
});
retryWithMergedOptions({ proxyUrl: newProxyUrl });
},
],
},
});
// リクエストの送信
async function fetchWithRotation(url) {
const session = randomUUID().slice(0, 8);
const proxyUrl = buildProxyUrl({ country: 'US', session });
const response = await client({
url,
proxyUrl,
timeout: { request: 15000 },
});
return response.body;
}
// 実行
const html = await fetchWithRotation('https://example.com');
console.log(html.slice(0, 200));
この例では、beforeRetryフックでリトライ時に新しいセッションIDを生成し、自動的にIPを切り替えています。これにより、特定のIPがブロックされた場合でも、次のリトライは別のIPから行われます。バックオフ制限を5000msに設定し、リトライ間隔を指数関数的に増やしながらサーバーに過負荷をかけない設計にしています。
プロダクションパターン
並行制御 with p-limit
スクレイピングをスケールする際、並行リクエスト数を制御することが重要です。p-limitを使うと、同時実行数を簡単に制限できます:
import pLimit from 'p-limit';
const limit = pLimit(10); // 最大10並行
const urls = [
'https://example.com/page/1',
'https://example.com/page/2',
'https://example.com/page/3',
// ...
];
const results = await Promise.all(
urls.map(url => limit(() => fetchWithRotation(url)))
);
並行数を10に設定することで、ターゲットサーバーに過度な負荷をかけず、かつ効率的にデータを収集できます。residential proxyの場合、ProxyHatのプランでサポートされる同時セッション数を超えないように調整してください。100並行で実行しても成功率が下がる場合は、5〜10並行に減らすことで安定するケースが多いです。
Cookie jarによるセッション維持
ログインが必要なサイトや、セッションCookieに依存するサイトでは、tough-cookieを使ってCookie jarを共有できます。セッションIDを固定することで、同じIPとCookieの組み合わせを維持できます:
import { gotScraping } from 'got-scraping';
import { CookieJar } from 'tough-cookie';
const cookieJar = new CookieJar();
const client = gotScraping.extend({
cookieJar,
proxyUrl: buildProxyUrl({ country: 'US', session: 'persist-001' }),
headerGeneratorOptions: {
browsers: ['chrome'],
devices: ['desktop'],
operatingSystems: ['windows'],
},
});
// ログイン
await client.post('https://example.com/login', {
json: { username: 'user', password: 'pass' },
});
// 認証済みページにアクセス(Cookieが自動的に送信される)
const dashboard = await client('https://example.com/dashboard');
これはIPベースの認証チェックを行うサイトで特に重要です。セッションIDを固定しておけば、CookieとIPの両方が一貫して維持されます。
CrawleeのCheerioCrawlerへの統合
より大規模なクローリングが必要な場合は、ApifyのCrawleeフレームワークのCheerioCrawlerを使用できます。CheerioCrawlerは内部でgot-scrapingを使用しており、プロキシ設定をそのまま引き継げます:
import { CheerioCrawler, ProxyConfiguration } from 'crawlee';
const proxyConfiguration = new ProxyConfiguration({
proxyUrls: [
buildProxyUrl({ country: 'US', session: 'crawl-001' }),
buildProxyUrl({ country: 'US', session: 'crawl-002' }),
buildProxyUrl({ country: 'US', session: 'crawl-003' }),
],
});
const crawler = new CheerioCrawler({
proxyConfiguration,
maxConcurrency: 10,
requestHandler: async ({ $, request }) => {
const title = $('title').text();
const links = $('a').map((_, el) => $(el).attr('href')).get();
console.log({ url: request.url, title, linkCount: links.length });
},
});
await crawler.run(['https://example.com']);
Crawleeを使うと、キュー管理、重複排除、エラーハンドリングが自動化されるため、数千ページのクローリングでも管理しやすくなります。SERPトラッキングのような大規模な定期収集にも適しています。
afterResponseフックによるエラー処理
gotのafterResponseフックを使うと、レスポンスを受け取った後に追加の処理を行えます。例えば、HTTP 200でもCAPTCHAページが返ってくるケースを検出してリトライできます:
const client = gotScraping.extend({
hooks: {
afterResponse: [
async (response, retryWithMergedOptions) => {
const body = response.body;
// CAPTCHAページの検出
if (typeof body === 'string' && body.includes('captcha')) {
const newSession = randomUUID().slice(0, 8);
return retryWithMergedOptions({
proxyUrl: buildProxyUrl({ country: 'US', session: newSession }),
});
}
return response;
},
],
},
});
このパターンは、HTTPステータスコードが200でもコンテンツがCAPTCHAやブロックページであるケースに対応できます。ステータスコードベースのリトライだけでは不十分なサイトで特に有効です。
ヘッドレスブラウザが必要なケース
got-scrapingは高速で効率的ですが、以下のケースではヘッドレスブラウザ(PlaywrightやPuppeteer)が必要になります:
- JavaScriptレンダリング: コンテンツがクライアントサイドでレンダリングされるSPA(React、Vue、Angular)
- 動的チャレンジ: Cloudflare TurnstileやreCAPTCHA v3など、ブラウザ環境の完全な実行を要求するチャレンジ
- インタラクティブな操作: クリック、スクロール、フォーム入力が必要なページ
ただし、ヘッドレスブラウザは1リクエストあたり500ms以上のオーバーヘッドがあるため、got-scrapingで十分な場合はそちらを優先すべきです。両方を組み合わせる戦略も有効です:got-scrapingで初期ページを取得し、必要な場合のみヘッドレスブラウザにフォールバックします。コンテナ化されたヘッドレスフリートを構築する場合は、PlaywrightのDockerイメージをベースにし、各コンテナにProxyHatのプロキシを環境変数で注入することで、スケーラブルなレンダリング基盤を構築できます。
倫理と法令遵守
スクレイピングを行う前に、以下を確認してください:
- robots.txtの確認: 対象サイトの
/robots.txtを確認し、クロールが許可されているパスのみにアクセスする - 利用規約の確認: 対象サイトのToSでスクレイピングが禁止されていないか確認する。CFAAは「認証を伴わずにアクセス制限を超える行為」を違法とする可能性があります
- レート制限の尊重: 1秒あたりのリクエスト数を合理的な範囲(1〜5 req/sec程度)に保つ
- 個人情報の取り扱い: GDPR(EU)やCCPA(カリフォルニア)の対象となる個人データを収集・保存する場合は、適切な法的根拠が必要
- 公式APIの優先: 対象サイトが公式APIを提供している場合は、そちらを優先する
詳細なプロキシ設定のガイドラインについては、ProxyHatのドキュメントも参照してください。
Key Takeaways
- got-scrapingはヘッダー生成を自動化: Sec-CH-UA、Accept-Language、ヘッダー順序をブラウザと整合させることで、WAFの第一防御線を突破できる
- residential proxyがIP信頼性を担保: ヘッダーが完璧でもデータセンターIPはブロックされる。residential proxyはISP登録済みのIPでこの問題を解決する
- セッションIDでスティッキーセッション:
user-country-US-session-abc123形式で同一IPを維持し、ログイン状態やレート制限に対応する - beforeRetryフックで自動IP切り替え: リトライ時に新しいセッションIDを生成することで、ブロックされたIPを自動的に回避する
- p-limitで並行制御: 10並行程度から始め、ターゲットサーバーのレスポンスと成功率を見ながら調整する
- Crawleeでスケール: CheerioCrawlerは内部でgot-scrapingを使用するため、プロキシ設定をそのまま引き継げる
- 倫理を優先: 公開データのみを対象とし、robots.txtとToSを尊重し、公式APIを優先する






