Most advice about JavaScript-heavy sites stops at the first decision(JavaScriptの多用されたサイトに関するほとんどのアドバイスは、最初の決断で止まってしまう)というのは正しくないので、通常の翻訳を行います。
JavaScriptを多用するサイトに関するアドバイスの大半は、最初の決断で止まってしまう。そのページに本当にブラウザが必要かどうか、という決断だ。この問いは重要であり、when do you actually need a headless browser to scrapeで詳しく答えている。「JavaScriptサイト」だと思われていたものの意外に多くが、実際にはブラウザを必要としないことが分かる。
このガイドは、その記事が終わった地点から始まる。対象のページが本当にレンダリングを必要とすることは確認済みだとする。ここで、コストと当番体制を最初の決断よりもはるかに大きく左右する、二つ目の決断がある。ブラウザを自分で運用するのか、それともページをウェブスクレイピングAPIに渡し、ブラウザの実行を任せるのか、という決断だ。
First, confirm the page really needs rendering
どちらの道を選ぶ前にも、簡単な確認をしておくべきだ。レンダリングは常により遅く、より重い選択肢だからだ。
ソースとレンダリング後のページを比較する。 生のHTMLレスポンスを開いてみる。欲しいデータがそこに含まれているなら、ブラウザは不要だ。
埋め込まれた状態を探す。 多くのJavaScriptフレームワークは、ブラウザが追加リクエストなしにページをハイドレートできるように、初期ページデータをscriptタグ内のJSONとして出荷している。欲しいデータがそのJSONに含まれているなら、通常のHTTPリクエストとJSONのパースだけで十分だ。
ネットワークリクエストを観察する。 ページがロード後にJSONエンドポイントからデータを取得している場合、そのエンドポイントに直接リクエストする方が、ページ全体をレンダリングするより通常は安価だ。
これら3つのいずれからもデータが得られない、あるいはエンドポイントが署名付き・不透明・再現不可能な形で保護されている場合は、レンダリングが必要になる。以下、読み進めてほしい。
What running your own browsers actually involves
ノートパソコン上のヘッドレスブラウザは簡単だ。しかし本番環境でのブラウザ群は運用上の課題であり、そのコストはプロトタイプには一切現れないため、過小評価しやすい。
キャパシティ。 各ブラウザインスタンスはメモリを大量に消費するため、1台のマシンで動かせる数に上限が生まれ、並行実行数がそのままインフラのコストに直結する。
安定性。 ブラウザは、敵対的なページに対してハングし、メモリリークし、クラッシュする。本番環境のブラウザ群には、ウォッチドッグ、リサイクル、そしてワーカーがページの途中で死んでも生き残るキューが必要だ。
メンテナンス。 ブラウザのバージョン、自動化ライブラリ、そして自動化のフィンガープリントはすべて変化していく。前四半期まで動いていたブラウザ群が、あなたのコードが一行も変わっていないのに動作しなくなることもある。
プロキシ。 レンダリングされたトラフィックにも、良質なIPが必要であり、認証とコンテキストごとの分離を伴って正しくブラウザに組み込む必要がある。これを正しく行うこと自体が一つの作業になる。using residential proxies with Playwrightを参照してほしい。
ブロックとチャレンジ。 CAPTCHAやアンチボットのチェックには検出、処理、リトライが必要であり、失敗した試行でも、その分のブラウザ時間は消費される。
リトライと待機。 動的なページがいつロードを完了したか、そしてまだ完了していない場合に何をすべきかを知ることは、サイトごとに書いて保守するロジックだ。
これらはどれも特殊なことではない。単に、誰もクライアントに請求しない作業であり、対象が増えるたびに増大していく。
What a web scraping API takes off your hands
ウェブスクレイピングAPIは、このリストをリクエストパラメータに変換する。Shifter Web Scraping APIの場合は次のようになる。
- レンダリング。
render_js=1はヘッドレスChromeでページを実行し、静的な取得と同じ1クレジットで課金される。 - 待機。
wait_for_cssはセレクタが現れるまでキャプチャを保持し、timeoutはページごとのブラウザ時間の上限を設定する。 - インタラクション。
js_instructionsは、キャプチャ前にscrollTo、click、waitの一連のステップを実行し、クッキーバナー、もっと読み込むボタン、スクロールで発生するコンテンツをカバーする。 - 構造化出力。
extract_rulesはCSSセレクタからJSONを返すため、パーサーを別途デプロイする必要がない。 - リトライ。 失敗した取得、CAPTCHA、一時的な対象側のエラーは、異なるプロキシを使って最大3回まで自動的にリトライされる。
- チャレンジ。 ステルスモードはデフォルトで有効であり、reCAPTCHAとhCaptchaはレンダリングされたリクエストの実行中に処理される。
- IP。 Growthプラン以上では、グローバルなジオロケーションを備えたレジデンシャルIPおよびモバイルIPのプールを経由する。Starterでは米国とEUのデータセンターIPを使用し、保護されていない多くのページにはこれで十分だ。
- セッション。
session_idは、マルチステップのフロー全体でクッキー、ブラウザ状態、そしてアップストリームのIPを保持し、10分間アイドルになると失効する。 - 課金。 成功したレスポンス1件につき1クレジット。失敗したリクエスト、対象側のエラー、そしてAPI自身によるリトライには課金されない。
並行実行数はプランごとに上限があり、Starterの20からEnterpriseの500まで幅があり、上限を超えるリクエストには429が返される。パラメータの全リファレンスはrendering JavaScriptから始まっている。
When your own browsers are still the right call
APIが常に答えというわけではなく、それが当てはまらない場面についてはっきりさせておく価値がある。
長時間の対話的セッション。 ブラウザを長時間サイト上に留めておく必要があり、セッションのアイドル許容時間を超える一時停止が発生するフローは、自分自身のブラウザの方が適している。
任意のブラウザロジック。 ページがカスタムスクリプト、拡張機能、あるいはスクロール・クリック・待機を超えるインタラクションを必要とする場合、自分で制御するブラウザの方が柔軟だ。
自社インフラが要件になっている場合。 一部のワークロードは、契約上またはセキュリティ上の理由で、特定のネットワークや環境内で実行しなければならない。
非常に大規模で非常に安定したボリューム。 ほとんど変化しない対象に対して十分な規模がある場合、自社所有のインフラは1ページあたりのコストを下げられる可能性がある。ただし、それを運用できるエンジニアが既にいることが前提だ。
テストとデバッグ。 ビジュアルリグレッション、ステップ実行でのデバッグ、そして人間がブラウザを見ている必要がある作業は、自分のマシン上で行うべきだ。
A decision table per target
この選択は会社単位ではなく対象単位で行うべきだ。成熟したスクレイピングスタックの大半は、これら3つの経路すべてを使っている。
| Target looks like | Best path |
|---|---|
| Data in the raw HTML or embedded JSON | Plain HTTP request |
| Data from a replayable JSON endpoint | Plain HTTP request to the endpoint |
| Rendered content, unprotected, low volume | Either; an API is less work |
| Rendered content behind anti-bot checks | Web scraping API |
| Rendered content across many markets | Web scraping API with per-request country |
| Long authenticated sessions or custom browser logic | Your own browsers with residential proxies |
| Huge, stable volume with an in-house team | Your own browsers, evaluated on total cost |
Compare on cost per usable page
この判断でよくある間違いは、APIのリクエスト単価をサーバーのコストと比較し、サーバーの方が安いと結論づけることだ。
公正な比較は、使用可能なページ1件あたりのコストだ。自社のブラウザ群の場合、これにはアイドル状態やクラッシュしたブラウザの計算コスト、失敗した試行に使われたプロキシの帯域、そしてメンテナンス、リトライ、チャレンジ処理に費やされたエンジニアリング時間が含まれ、これらを実際に正しいデータを生成したページ数で割ったものになる。成功したレスポンスにのみ課金されるAPIの場合、失敗には課金されないため、1クレジットあたりの価格は使用可能なページ1件あたりの実際のコストにかなり近い。ただし、セレクタの変更によって空のフィールドが返されるなど、成功はしたが実際には無用なページについては、依然として課金される。
実際の対象URLの同じサンプルを1週間、両方の経路で流し、正しく完全なデータを返したページ数を数え、使用可能なページ1件あたりのコストを比較してみる。この数字は、どんな機能一覧よりも早く議論を決着させる。APIのプランはpricing pageに掲載されている。
Where the two meet
この選択は、単一のサイトに対してさえ二択ではない。レンダリングを必要としないページには通常のHTTPを使い、レンダリングが必要で保護されたページはAPIに送り、カスタムロジックが必要な少数のフローのためだけに小規模なブラウザ環境を維持する、というのが一般的で合理的なパターンだ。
無限スクロールともっと読み込むフィードは、まさにこの境界線上にあり、レンダリング内でそれらを処理する手法はhow to handle infinite scroll and dynamic paginationで扱っている。APIがこれらすべてを1つのリクエストの裏側でどのように組み立てているかは、how does a web scraping API workで説明している。
FAQ
Is rendering more expensive with an API?
レイテンシの観点では、そうだ。ブラウザは通常のリクエストより時間がかかるからだ。クレジットの観点では、そうではない。レンダリングされたリクエストは、静的な取得と同じ1クレジットのコストだ。
Can an API handle every anti-bot system?
すべてを、常に処理できるわけではない。ほとんどのチェックは処理されるが、一貫してブロックされる対象については、サポートが調整できる場合がある。依存する前に、自分の対象で自分自身の成功率を測定してほしい。
Do I still need proxies if I use an API?
別途プロキシを用意する必要はない。APIはリクエストを自社のプール経由でルーティングし、Growthプラン以上ではレジデンシャルおよびモバイルのプールを使用する。
When should I move from an API to my own browsers?
APIが提供していないブラウザの挙動が必要な場合、あるいは実際のボリュームで測定した使用可能なページ1件あたりのコスト比較が、それを運用するエンジニアリングも含めて自社インフラの方に有利な場合だ。
The bottom line
サイトがJavaScriptのレンダリングを必要とすると判断するのは、簡単な方の半分だ。高くつくのは、誰がブラウザを運用するかを決める方の半分だ。自社のブラウザ群は柔軟性を買うが、その代償はキャパシティ、安定性、メンテナンス、プロキシ、そしてチャレンジ処理として支払うことになる。APIはその柔軟性をパラメータ、リトライ、そして成功時のみの課金と引き換える。
ページが本当にレンダリングを必要とするか確認し、会社単位ではなく対象単位で選択し、そして自社構築か購入かの問いは、測定された使用可能なページ1件あたりのコストで決着させてほしい。この製品はWeb Scraping APIページにある。