Playwrightで不必要なJSやCSSなどをファイル単位で狙い撃ちして無効化する方法(プロキシ帯域幅の節約にも役立つ!)
node-fetchでスクレイピングできないサイトは、仕方なくPlaywrightを使って実行しています。とても便利なのですがプロキシを刺しながらだと帯域幅を喰いすぎてしまい、プロキシ帯域幅の追加課金するしかありません。低コストでスクレイピングするにはPlaywrightは向いていない。
そう思っていませんか?
- 
hoge.jsは動かしたいけど、ageage.jsは要らないなぁ。 - 
fuga.cssは必要だけど、sagesage.cssは要らないなぁ。 
※ファイル名を指定してリクエストしないファイルを狙い撃ちしたい。
なんと、Playwrightではこれが実現できちゃいます!
しかも不要なリソースはリクエスト自体が投げられないので帯域幅の節約になります!
この記事でできること
例として REIRIEちゃんの公式サイト で説明します。

上記のようにJSファイルが846kbあります。
このうち、 lodash.min.js だけを受信許可して、下記のように帯域幅を大幅に削減する方法の紹介です。

846kb → 25.5kb で、97%も帯域幅を削減できてしまいました!
 実際にやってみよう!( .route() を使おう )
※本記事では下記記事を踏襲した書き方をしています。中身はただのPlaywrightですので、適宜読み替えてください。
import { chrome } from "@utils/functions/browser";
(async () => {
  const { page } = await chrome();
  await page.route(/(\.js.*|\.css.*)/, (route) => {
    // 各リソースのリクエストURLを取得
    const url = route.request().url();
    if (url.match(/lodash\.min\.js/)) {
      // loadash.min.js にマッチしたURLだけ
      // 通信を許可する
      return route.continue();
    }
    // リクエストを発生させず破棄する
    // つまりこの例ではloadash.min.js以外のJSとCSSファイルのリクエストを全て破棄
    return route.abort();
  });
  await page.goto("https://www.reirieofficial.com/");
  // この下にいつも通りにスクレイピングするコードを書いていく。
})();
/(\.js.*|\.css.*)/ は正規表現で記述でき、 hoge.js?20240401 や hoge.css?20240401 などのキャッシュバスティングもリーチするようにしています。
if (url.match(/lodash\.min\.js/)) { で loadash.min.js を狙い撃ちしています。
return route.continue() まで到達したURLはリクエスト発生させます。
return route.abort() に到達したURLはリクエストを破棄します。
絞り込みのコツ
まずは
  await page.route(/(\.js.*|\.css.*)/, (route) => {
    return route.abort();
  });
でとにかく全てのJSを無効にしてみるのがオススメです。
JSから更にJSをリクエストする形で呼び出されるサイトも多く、何が起点に動いているかが追求しやすくなります。
まとめ:Playwrightでも超低コストなスクレイピングが実現可能!
Playwrightでもプロキシを刺してても低コストなスクレイピングが可能なことがおわかりいただけたかと思います。不要なリソースは削りまくって帯域幅をどんどん節約していきましょう!
ただし、JSファイルによっては芋づる式にリクエストしたり、動作に影響が出るものもあります。よく検証した上で実行するようにしましょう。
ちなみに...通信量を大きく減らせるのでスクレイピング速度がかなり向上します
リクエスト数が減って通信量が減るのでページ表示速度がかなり早くなります。Playwrightを使っていても高速なスクレイピングが実現できるのは嬉しいですね!
他にもこんな記事を書きました。
安価で高品質なプロキシリストをお探しであれば、 WebShare をオススメします!僕もスクレイピングの際は欠かさず利用しています。月3ドル以下でめちゃくちゃスクレイピングできます!
もっと帯域幅を削減したいならWebフォントや画像の無効化も行いましょう。下記記事に掲載してあります。
おまけ
再帰処理を実装する際に気をつけるべきと思われる事象に遭遇しました。
どうやら page.route は page.unroute で適切に解放してあげないと CPUとメモリを圧迫してしまうようです。使用率95%とかまで行ってしまいスクレイピング速度が低下しました。(検証できてないのでpage.route が原因かは明確ではありません。)
僕は await browser.close() で一定周期で throw new Errorを投げて例外処理の中でインスタンスを丸ごと破棄してPlaywrightを再起動する処理にしました。これを実装した結果、CPUとメモリが適切に解放されスムーズなスクレイピングが行えるようになりました。
※unrouteの実装をやってみたのですが解決されず...。await browser.close(); でいいと思います。
Discussion