Socks5Proxy

Puppeteerプロキシ設定、--proxy-serverとpage.authenticateで接続する方法

このガイドではPuppeteerでChromeを実行する際、Socks5ProxyのHTTPプロキシを--proxy-server引数とpage.authenticate認証で接続する方法、SOCKS5接続、複数IPでブラウザを分けて起動するコードを説明します。

Puppeteerでプロキシはどのように動作しますか?

PuppeteerはChromeを実行する際に渡す引数でプロキシを設定します。--proxy-serverにアドレスを設定すると、そのブラウザのすべてのページが同じプロキシを使用します。HTTPプロキシのユーザー名とパスワードは実行引数で設定できないので、ページを開いた後にpage.authenticateで渡します。これにより、プロキシが認証を要求したときにPuppeteerが自動的に応答します。

例ではIP 198.51.100.23、HTTPポート8000、SOCKS5ポート1080、ユーザー名spx_user、パスワードspx_passを使用します。

npm install puppeteer

HTTPプロキシと認証はどうやって接続しますか?

launchのargsに--proxy-server=198.51.100.23:8000を入れ、newPageでページを作成した直後にpage.authenticateにユーザー名とパスワードを渡します。認証はページごとに行うため、新しいページを作成するたびに同じ呼び出しを行ってください。

HTTPSサイトもそのまま開きます。ブラウザがCONNECTトンネルを作成し、その中で暗号化された状態で通信します。

import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  headless: true,
  args: ["--proxy-server=http://198.51.100.23:8000"],
});

const page = await browser.newPage();
await page.authenticate({ username: "spx_user", password: "spx_pass" });

await page.goto("https://api.ipify.org", { waitUntil: "domcontentloaded" });
console.log(await page.evaluate(() => document.body.innerText)); // proxy IP

await browser.close();

SOCKS5プロキシはどのように使用しますか?

--proxy-serverにsocks5://アドレスを指定します。ただしChromeはSOCKSプロキシのユーザー名・パスワード認証をサポートしていないため、page.authenticateはSOCKS5には適用されません。SOCKS5で接続するには、ダッシュボードでこのコードが実行されるサーバーのIPv4を登録するIP認証を使用してください。

ユーザー名とパスワードで接続する必要がある環境では、同じIPのHTTPポートを使用するのが最も簡単です。同じIPのため対象サイトから見える位置は同じです。

import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  args: ["--proxy-server=socks5://198.51.100.23:1080"], // server IP is authorized in the dashboard
});
const page = await browser.newPage();
await page.goto("https://api.ipify.org");
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();

複数のIPでブラウザを分けて起動するにはどうすればいいですか?

Chromeは実行されたブラウザ1つにプロキシ1つなので、異なるIPが必要な場合はブラウザを別々に実行します。以下のコードはAPIでip:port:user:passリストを受け取り、最初の3つでブラウザを1つずつ起動し、各ページで表示されるIPを出力します。

ブラウザを多く起動するとメモリを多く消費します。タスクキューを作成し、同時に実行するブラウザ数を制限することをお勧めします。

import puppeteer from "puppeteer";

const KEY = "YOUR_API_KEY";
const res = await fetch("https://socks5proxy.io/api/v1/proxies/download?format=ip:port:user:pass&protocol=http", {
  headers: { Authorization: "Bearer " + KEY },
});
const lines = (await res.text()).trim().split("\n").slice(0, 3);

await Promise.all(lines.map(async (line) => {
  const [ip, port, user, pass] = line.trim().split(":");
  const browser = await puppeteer.launch({ args: ["--proxy-server=http://" + ip + ":" + port] });
  try {
    const page = await browser.newPage();
    await page.authenticate({ username: user, password: pass });
    await page.goto("https://api.ipify.org", { timeout: 30000 });
    console.log(ip, await page.evaluate(() => document.body.innerText));
  } finally {
    await browser.close();
  }
}));

ページをもっと速く開くにはどうすればいいですか?

プロキシ経由では画像、フォント、動画まで全てプロキシ経由で受信するため、帯域幅と時間がより消費されます。必要なものがテキストとデータのみの場合は、page.setRequestInterceptionで画像とフォントリクエストをブロックすると速度が向上し、共有プランの月間帯域幅も節約できます。

waitUntil値も重要です。networkidle0はすべてのリクエストが終わるまで待つため遅くなる可能性があるので、必要な要素が表示されたらすぐに次のステップに進むためにpage.waitForSelectorを使用するのが良いです。

await page.setRequestInterception(true);
page.on("request", (req) => {
  const type = req.resourceType();
  if (type === "image" || type === "font" || type === "media") req.abort();
  else req.continue();
});

ブラウザ1つでページごとに異なるIPを使用できますか?

Chromeの--proxy-serverはブラウザ全体に適用されるため、ページごとに異なるIPを提供するのが難しいです。Puppeteer 22以上はbrowser.createBrowserContextにproxyServerオプションを追加し、コンテキストごとに異なるプロキシを使用できます。コンテキストはクッキーとストレージが分離されているため、アカウントごとの作業にも適しています。

コンテキストで作成したページもpage.authenticateでユーザー名とパスワードを渡せばOKです。ブラウザを複数立ち上げるよりもメモリを節約できます。

import puppeteer from "puppeteer";

const browser = await puppeteer.launch();
const context = await browser.createBrowserContext({ proxyServer: "http://198.51.100.23:8000" });
const page = await context.newPage();
await page.authenticate({ username: "spx_user", password: "spx_pass" });
await page.goto("https://api.ipify.org");
console.log(await page.evaluate(() => document.body.innerText));
await context.close();
await browser.close();

その国のユーザーのように見せるためには何を合わせればいいですか?

IP国に合わせてpage.setExtraHTTPHeadersでAccept-Languageを、page.emulateTimezoneでタイムゾーンを設定してください。言語とタイムゾーンがIP国と異なると、ページが変な言語で表示されたり日付表示が変わることがあります。

画面サイズはpage.setViewportで設定し、スマートフォン画面が必要な場合はpuppeteerのKnownDevicesにあるデバイス設定をpage.emulateに渡します。

await page.setExtraHTTPHeaders({ "Accept-Language": "ja-JP,ja;q=0.9" });
await page.emulateTimezone("Asia/Tokyo");
await page.setViewport({ width: 1366, height: 900 });

長時間実行する作業でブラウザを安定させるにはどうすれば良いですか?

一日中動かす作業は、ブラウザを一度起動して使い続けるより、一定数のページを処理したら閉じて新しく起動する方が安定します。長時間起動したままのブラウザはメモリを徐々に消費し、一度エラーが発生すると以降の作業がすべて停止する可能性があります。

ページごとにtryとfinallyでpage.closeを必ず呼び出し、gotoにはtimeoutを設定して応答のないページで止まらないようにしてください。失敗した作業は別のプロキシで新しいブラウザを起動して再試行すると良いです。

プロキシ応答時間とエラーをプロキシごとに記録しておくと、遅い回線を特定し、ダッシュボードのカスタマーサポートに問い合わせたり、作業バッチを変更する際に役立ちます。

よくあるエラーはどう解決しますか?

net::ERR_INVALID_AUTH_CREDENTIALSまたは407はpage.authenticateの値が間違っているか、newPageの後に呼び出していない場合です。各ページでgotoの前に呼び出したか確認してください。

net::ERR_PROXY_CONNECTION_FAILEDはアドレスやポートが間違っている場合です。HTTPアドレスにHTTPポート、SOCKS5アドレスにSOCKS5ポートを使用しているか確認してください。SOCKS5で接続が拒否された場合は、IP認証にサーバーの送信IPが登録されているか確認してください。

Puppeteerプロキシ設定、page.authenticate認証 | Socks5Proxy