Puppeteerは実際のChromiumを操作するため、ターゲットがJavaScriptでコンテンツをレンダリングする場合、データをインタラクションの背後に隠している場合、あるいは実際のブラウザでないものをフィンガープリントする場合に、まさに求められる選択肢となります。レジデンシャルプロキシの追加は数行で済みますが、Puppeteerは作業を2つの異なる場所に分割するため、ほとんどの人が最初につまずくポイントがあります。プロキシアドレスは起動時の引数に入り、認証情報はまったく別の場所に入るのです。
これはPlaywrightでのレジデンシャルプロキシEntryに続くPuppeteer編です。フルブラウザではなくシンプルなHTTPクライアントを使いたい場合はNode.jsでのプロキシを参照してください。ここではブラウザ特有の落とし穴に焦点を当てます。
以下はすべてShifterのレジデンシャルゲートウェイを使用しています。エンドポイントはp.shifter.io:443の1つのみで、すべてのターゲティングはユーザー名にエンコードされています。ホストと認証情報を別のプロバイダーのものに置き換えても、構造は同じです。
ゲートウェイモデルを一段落で理解する
プロキシのユーザー名には、認証情報とターゲティング情報の両方が含まれています。国やセッションを変更する際にエンドポイントを切り替えるのではなく、ユーザー名の文字列を変更します。
customer-USERNAME-country-us-sid-abc123-ttl-600country-usはアメリカ合衆国をターゲットにし、sidはスティッキーセッションを固定し、ttlはそのIPをN秒間保持します。sid/ttlを省略すると、新しい接続のたびにローテーションします。パスワードは一定のままです。Puppeteerでは、ホストは起動フラグに入り、そのユーザー名はpage.authenticateに入ります。
セットアップ:起動引数のプロキシ、page.authenticateの認証情報
Chromiumはプロキシサーバーをコマンドラインフラグ--proxy-serverとして受け取り、Puppeteerのargsを通じて渡されます。ここにuser:pass@hostを指定することはできません。Chromiumはそのフラグから認証情報を読み取らないからです。代わりに、page.authenticateを使ってページごとに認証情報を提供します。これはプロキシの407チャレンジに対して正しいProxy-Authorizationで応答します。
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true, args: ['--proxy-server=http://p.shifter.io:443'], // ホストのみ、認証情報なし});
const page = await browser.newPage();await page.authenticate({ username: `${process.env.SHIFTER_USER}-country-us`, // ターゲティングはここに存在する password: process.env.SHIFTER_PASS,});
await page.goto('https://api.ipify.org');console.log(await page.evaluate(() => document.body.innerText)); // アメリカのレジデンシャルIPawait browser.close();理解すべきことが2つあります。ユーザー名にはターゲティングフラグ(-country-us)が含まれます。ジオ情報はURLではなくそこにあるからです。そしてpage.authenticateは認証が必要なプロキシにとって省略できません。これがないと、すべてのナビゲーションが407で失敗します。この分割、つまりフラグ内のホストとpage.authenticate内の認証情報という構造は、Puppeteer-プロキシに関する最も一般的な間違いです。
1つのゲートウェイを通じてジオとセッションをローテーションする
これがその設計から得られる有用な帰結です。プロキシのホストはブラウザ起動時に固定され、ページごとに変更することはできませんが、その必要もありません。ターゲティングがユーザー名にあり、page.authenticateがページごとに設定されるため、各ページに異なるユーザー名を与えることで、同じゲートウェイ上で異なるアイデンティティを通じてルーティングされます。1つのブラウザで、多数のジオを、再起動なしで扱えます。
async function pageFor(browser, country, sid) { const page = await browser.newPage(); const user = `${process.env.SHIFTER_USER}-country-${country}` + (sid ? `-sid-${sid}-ttl-600` : ''); await page.authenticate({ username: user, password: process.env.SHIFTER_PASS }); return page;}
const de = await pageFor(browser, 'de', 'job-42'); // ドイツのスティッキーセッションconst us = await pageFor(browser, 'us', null); // ローテーティングのアメリカ作業の各論理単位に独自のsidを与え、セッションの途中ではなく単位間でローテーションしてください(スティッキー対ローテーティングがその違いを扱っています)、そして負荷分散に関する記事が説明する方法で作業をアイデンティティにマッピングしてください。アイデンティティ間でクッキーとストレージを分離するには、それぞれを独自のブラウザコンテキストに配置します。
const context = await browser.createBrowserContext(); // 分離されたクッキー/ストレージconst page = await context.newPage();await page.authenticate({ username: userFor('gb'), password: pass });名前に注意してください。最近のPuppeteerではこれをcreateBrowserContext()と呼びますが、古いバージョンではcreateIncognitoBrowserContext()と呼ばれていました。考え方は同じで、名前が変わっただけです。
落とし穴1:ブラウザを再利用する、リクエストごとに起動しない
Chromiumの起動は重く、リクエストごとに新しいブラウザを起動すると、起動のたびに数百ミリ秒のオーバーヘッドと実際のメモリ消費が発生します。これはレイテンシガイドが解消しようとしているオーバーヘッドです。起動時に1つのブラウザを起動して再利用し、並行作業のためにページやコンテキストを開き、完了したら閉じてください。1つの長寿命ブラウザの下でページのプールを持つのが正しい形です。
落とし穴2:不要なリソースをブロックする
ブラウザは実際のブラウザが取得するすべてのものを取得します。画像、フォント、メディア、スタイルシート、分析ツールなどです。HTMLやいくつかのフィールドだけが必要な場合、それは無駄に支払っている帯域幅であり、待たされている時間です。Puppeteerではリクエストをインターセプトし、不要なものを中止することができ、これによりページ読み込み時間と帯域幅コストの両方を大幅に削減できます。
await page.setRequestInterception(true);page.on('request', (req) => { const blocked = ['image', 'font', 'media', 'stylesheet']; if (blocked.includes(req.resourceType())) req.abort(); else req.continue();});選別的に行ってください。サイトによっては、CSSや特定のスクリプトがないと、求めているコンテンツをレンダリングしないことがあります。積極的にブロックした上で、データが引き続き表示されるか確認してください。表示される場合、これはヘッドレスブラウザで利用できる最も安価な高速化手段です。
落とし穴3:ブラウザはブロック回避の半分にすぎない
レジデンシャルIPは人間らしく見えることのネットワーク側の半分を担いますが、Puppeteerはそれでもヘッドレスのchromiumを操作しており、サイトはブラウザ自体もフィンガープリントします。navigator.webdriver、ヘッドレス特有の癖、自動化のシグナルなどです。良好なレピュテーションを持つクリーンなIPは多くのチャレンジを回避できますが、明らかに自動化されたブラウザを偽装することはできません。最新のPuppeteerとChromiumを使い、古い簡単に検出されるモードではなく現代的なヘッドレスモードを利用し、ビューポートとユーザーエージェントを現実的に保ち、人間的なペースでページを操作してください。検出をトリガーする間違いはIP層と同じくらいブラウザ層にも当てはまり、両者は整合している必要があります。
落とし穴4:並行性に上限を設ける
開いているページはそれぞれ実際のブラウザタブであり、実際のメモリを保持するため、HTTPリクエストを一斉に送るのと同じように何千も開くことはできません。ページやコンテキストのプールに上限を設けて再利用し、ターゲットホストごとの進行中の作業数を制限してください。そうすれば、脆弱なサイトが叩かれすぎたり、許容度の高いサイトが飢餓状態になったりすることを避けられます。ターゲットの許容範囲を超えた並行性は、スループットではなくブロックとメモリ不足のクラッシュを招きます(ブロックを回避する方法)。
実際にプロキシ経由になっているか確認する
ベンチマークやその他のデバッグを行う前に、ページ内から出口IPを確認してください。
await page.goto('http://ip-api.com/json');console.log(await page.evaluate(() => document.body.innerText)); // ターゲットにした国が表示されるはず自分自身のIPが表示される場合、--proxy-serverフラグが適用されていません。407ダイアログでハングする場合、page.authenticateが欠落しているか、認証情報が間違っています。一般的なハングは、ローカルの発信がブロックされていることを示します。この3つはすべてタイムアウト診断ガイドで扱われています。
FAQ
なぜ--proxy-serverにuser:pass@hostを入れても機能しないのですか?
Chromiumは--proxy-serverフラグからプロキシの認証情報を読み取りません。そこにはホストのみを渡し、page.authenticate({ username, password })で認証情報を提供してください。これがプロキシの407チャレンジを処理します。ゲートウェイはターゲティングをユーザー名にエンコードするため、完全なユーザー名(-country-...を含む)がpage.authenticateに入ります。
プロキシが起動時に設定されている場合、ページごとに異なる国を使うにはどうすればよいですか?
プロキシのホストを変更するのではなく、page.authenticateのユーザー名を変更します。ターゲティングはユーザー名にあり、ゲートウェイのホストは一定のままなので、各ページは異なるユーザー名で認証し、異なるアイデンティティを通じて出て行くことができます。1つのブラウザが多数のジオに対応します。
ブラウザを再起動せずにプロキシをローテーションできますか?
アイデンティティとジオに関しては可能です。ページやコンテキストごとにpage.authenticateのユーザー名を変えることで実現します。本当に異なるプロキシホストが必要な場合にのみ再起動が必要になりますが、単一のゲートウェイエンドポイントではその必要はありません。
Puppeteerで帯域幅を削減するにはどうすればよいですか? リクエストインターセプションを有効にし、不要なリソースタイプ(画像、フォント、メディア、多くの場合スタイルシート)を中止してください。ターゲットが必要なデータを引き続きレンダリングすることを確認してから、そのブロックを維持してください。これはヘッドレスブラウザにおいて、速度とコストの両方に対する最大の単一のレバーです。
PuppeteerかPlaywrightか?
どちらも実際のブラウザを操作し、どちらもレジデンシャルプロキシとうまく機能します。Playwrightはコンテキストオプションでプロキシ(認証情報含む)を直接受け取ります。Puppeteerは起動フラグとpage.authenticateに分割します。エコシステムとの適合性や既存のコードで選んでください。プロキシの概念は同じです。
まとめ
Puppeteerとレジデンシャルプロキシの組み合わせは、その分割が理解できれば簡単です。プロキシのホストは起動時に--proxy-serverに入り、ユーザー名にターゲティング情報を運ぶ認証情報は、ページごとにpage.authenticateに入ります。再起動するのではなく、そのユーザー名を変えることでジオとセッションをローテーションし、ブラウザコンテキストでアイデンティティを分離し、1つの長寿命ブラウザを再利用し、不要なリソースをブロックし、ブラウザのフィンガープリントがIPと同じくらい人間らしく見える必要があることを忘れないでください。
これを正しく行えば、Puppeteerは通常のHTTPクライアントでは扱えないJavaScript重視のターゲットに対応できます。レジデンシャルゲートウェイに向けて設定し、プールの品質がどれだけチャレンジされるかを左右することを忘れないでください(IPレピュテーション)。料金ページにはGB単位のプランがあり、自分のターゲットで試すことができます。