レジデンシャルプロキシはギガバイト単位で課金されるため、GB単位のプランでは帯域幅がそのまま請求額になる。問題は、単純なスクレイパーが支払った分のバイトの大半を無駄にしていることだ。決して見ない画像、フォント、動画、トラッキングスクリプトをダウンロードし、変化していないページを再取得し、単純なHTTPリクエストで済むところをフルブラウザでレンダリングしてしまう。良い知らせは、こうした無駄のほとんどは簡単に削減でき、チューニングされたスクレイパーは同じデータを取得するのに、チューニングされていないものの半分以下の帯域幅で済むことが日常的にあるということだ。
これは、まさにそれを実現するための実践的なガイドである。影響の大きい順に並べた9つの戦術を紹介する。レジデンシャルプロキシを通じて同じデータを収集しながら、バイト数をごくわずかに抑えて支払う方法だ。これはbandwidth-priced residential proxiesで取り上げたGB単位課金への移行に関する、コスト効率面での補完記事である。
1. 必要でない限りブラウザをレンダリングしない
これが最大の一手だ。ヘッドレスブラウザはページが参照するあらゆるものをダウンロードする。画像、CSS、ウェブフォント、動画、アナリティクス、広告スクリプトなどだ。ほとんどのスクレイピングでは、これらは一切不要である。欲しいデータが初期HTMLに含まれているか、APIから取得できる場合(次のセクション)、ブラウザを完全にスキップし、通常のHTTPクライアント、requests、httpx、またはScrapyを使うべきだ。1回のHTTP GETは、フルブラウザレンダリングが引き込むバイト数のごく一部で済むことがある。
ブラウザは、データを生成するために本当にJavaScriptの実行が必要なページのために取っておこう。迷ったら、まず生のHTMLを取得し、目的の値がすでにそこにあるかどうかを確認すること。
2. HTMLではなくJSON APIを叩く
多くのサイトは、ページ読み込み後に呼び出すバックエンドのJSONエンドポイントからコンテンツをレンダリングしている。フルHTMLページとそのアセットの代わりに、そのエンドポイントを直接取得することで、バイト数がごくわずかに抑えられることが多く、パース不要のクリーンで構造化されたデータが得られる。ブラウザのネットワークタブを開き、データを返しているリクエスト(通常はJSONを返すXHR/fetch)を見つけ、それをプロキシ経由で直接呼び出そう。1つの軽量なJSONレスポンスが、重いページとブラウザレンダリングの両方を代替できる。
3. ブラウザを使わざるを得ない場合は、重いリソースタイプをブロックする
ページが本当にレンダリングを必要とする場合でも、使わないものをブラウザにダウンロードさせてはいけない。PlaywrightとPuppeteerはどちらも、画像、メディア、フォント、そして多くの場合スタイルシートが、プロキシに届く前にリクエストを傍受して中断させることができ、これによって通常は帯域幅を半分以上削減できる。Playwrightの場合:
await context.route("**/*", (route) => { const type = route.request().resourceType(); if (["image", "media", "font"].includes(type)) { return route.abort(); // never downloaded, never billed } return route.continue();});document、script、xhr、fetchは残し、それ以外は破棄する。同じパターンとChromium固有の詳細は、how to use residential proxies with Playwrightにある。
4. 圧縮を有効にしておく
テキストはおおよそ5倍から10倍に圧縮されるため、gzipまたはbrotliでエンコードされたHTMLレスポンスは生のサイズのごく一部になる。ほとんどのHTTPクライアントはデフォルトでAccept-Encoding: gzip, deflate, brを送信し、透過的に解凍するが、無効化していないか確認し、このヘッダーを削除しないこと。手動でヘッダーを設定している場合は、含めておこう:
headers = {"Accept-Encoding": "gzip, br", "User-Agent": "..."}プロキシを通過するバイトは圧縮後のものなので、これは請求額を直接、しかも無料で削減できる方法だ。
5. 条件付きリクエストを使い、変化していないページのコストをほぼゼロにする
定期的なクロールでは、無条件にページを再ダウンロードしてはいけない。HTTPの条件付きリクエストを使えば、サーバーは本文全体を送り返す代わりに、小さな304レスポンスで「変化なし」と伝えてくれる。各取得時のETagまたはLast-Modifiedを保存し、それを送り返そう:
# On refetch, send the stored validatorheaders = {"If-None-Match": stored_etag} # or If-Modified-Sincer = client.get(url, headers=headers)if r.status_code == 304: pass # unchanged: a few bytes instead of the full page304レスポンスはフルページに比べてほとんどコストがかからず、ほとんどのページがめったに変化しない監視ジョブでは、これだけで帯域幅を大幅に削減できる。
6. 変化していないものを再取得しない
関連するが、HTTPバリデータより広範な話として、すでに収集したものを追跡し、スキップすることが挙げられる。サイトマップのlastmodタイムスタンプを使って変更されたURLを見つけ、ページごとにコンテンツハッシュを保持して同一コンテンツの再処理をスキップし、クロールフロンティアを重複排除して、実行中に同じURLを二度取得しないようにしよう。最も安いギガバイトは、決してダウンロードしないギガバイトだ。
7. 必要なバイトだけを取得する
ファイルのヘッダーや先頭部分だけが必要な場合(メタデータの確認、content-typeの判別など)、ファイル全体をダウンロードしてはいけない。HEADリクエストは本文なしでヘッダーを返し、Rangeリクエストはサーバーが対応していれば指定したバイトだけを取得できる:
client.head(url) # headers only, no bodyclient.get(url, headers={"Range": "bytes=0-2047"}) # first 2 KB onlyこれは、クロールが大きなメディア、PDF、または一部だけ、もしくはメタデータだけが必要なダウンロードに触れる場合に、最も重要になる。
8. リトライとジオターゲティングを効率的にする
失敗した試行もすべて帯域幅を消費するため、無駄なリトライは静かに請求額を膨らませる。失敗しているURLを叩き続けるのではなく、バックオフしてリトライ回数に上限を設けよう。また、条件に合うIPが空いていないときにリクエストが失敗してリトライを繰り返す原因となる、過度に厳しいジオフィルター(国+都市+ASNをすべて同時指定するなど)は避け、代わりにフィルターを緩めること。停止したレスポンスが長引かないよう適切なタイムアウトを設定し、セッション戦略をジョブに合わせて選び、不必要にフローを再確立しないようにしよう。効率的なリクエストは、安いリクエストだ。
9. レコードあたりのバイト数を測定する
測定しないものは削減できない。ジョブごとの帯域幅を追跡し、さらに良いのは、抽出に成功したレコードあたりのバイト数を追跡することだ。そうすれば無駄が自然と見えてくる。1行あたりメガバイト単位のコストがかかっているページは、たいてい、ブロックできるはずのアセットを引き込んでいるか、APIを叩けば済むところをレンダリングしている。記録し、傾向を観察し、最も無駄の多いものから最適化しよう。
責任を持って行うことについての注意
帯域幅を削減することは、自分にとって安上がりであるだけでなく、収集対象のサイトにとっても負荷が軽くなる。リクエストが少なく小さいほど、サーバーへの負荷は小さくなる。これはコスト効率と良き市民であることを一致させる。公開データの収集は続けつつ、各サイトの利用規約とレート制限を尊重し、効率化によって自分のフットプリントを減らそう。Shifterで許可されている内容については、利用規約が正式な基準となる。
よくある質問
プロキシ経由のスクレイピングで最も帯域幅を使うものは何か? フルブラウザレンダリングが、大差をつけて最大だ。ページ上のあらゆる画像、フォント、動画、スクリプトをダウンロードするためである。レンダリングを削減すること(あるいはレンダリングが必要な場合は重いリソースタイプをブロックすること)が、最も効果の大きい変更だ。
画像やフォントをブロックするとスクレイピングが壊れるか? 通常は壊れない。データ抽出にそれらが必要になることはめったにない。対象のコンテンツが画像の裏で遅延読み込みされていないかテストすること。大多数のジョブでは、画像、メディア、フォントのリクエストをブロックしても安全で、帯域幅を大幅に削減できる。
レジデンシャルプロキシは失敗またはブロックされたリクエストにも課金するか? プロキシを通過したバイトは課金対象なので、失敗した試行やリトライも帯域幅のコストがかかる。だからこそ、効率的なリトライ、適切なタイムアウト、そして失敗の原因となる過度に厳しいジオフィルターを避けることが、請求額にとって重要になる。
サイトのJSON APIを叩くことは許されるか? ページ自体が使用している公開エンドポイントを呼び出すことは一般的な慣行であり、はるかに効率的だが、それでもサイトの利用規約とレート制限は適用される。責任を持って収集し、不確かな点については法的助言を得ること。
現実的にどれくらい節約できるか? 対象によって異なるが、不要なレンダリングをスキップし、重いリソースをブロックし、変化していないページを再取得しないことで、同じ抽出データに対して、総帯域幅を半分以上削減できることが一般的である。
まとめ
GB単位のプランでは、帯域幅の効率がそのままコストの効率であり、ほとんどのスクレイパーは多くを無駄にしている。取得で済むところをレンダリングし、決して読まないアセットをダウンロードし、変化していないページを再取得している。このリストに沿って取り組もう。可能な限りブラウザをスキップし、APIを叩き、重いリソースをブロックし、圧縮を有効にしておき、条件付きリクエストを使い、変化していないページを再取得せず、必要なものだけを取得し、リトライを効率化し、そして測定すること。そうすれば、同じデータをごくわずかなバイト数で取り戻せる。
この効率化は、プールの品質と相まって効果が積み重なる。クリーンなIPはブロックとリトライを減らし、それはすなわち無駄なバイトの削減につながる。だからこそ、クライアントのチューニングと合わせて、スクレイピングに適したプロキシを選ぶことが重要だ。料金ページでGB単位のプランを確認し、チューニング済みのスクレイパーをレジデンシャルゲートウェイに向けて、バイト数が下がるのを見てみよう。