🍆

Next.jsのサイトを阿部寛のホームページ級に軽量化してスクレイピングする【Playwright】

2024/04/11に公開

阿部寛のホームページは画像込みで41.7KBの超軽量サイトです。

さすがに阿部寛のホームページには敵いサイトばかりですが、阿部寛のホームページ級の軽量化を目指してスクレイピングする方法をご紹介します。

今回の例では 840KB → 20.8KB で97.52%も削減できました。

スクレイピングにおいて軽量化すると嬉しいこと

  • 余計な処理を減らせるのでマシンの負荷を下げられる。
  • 表示速度が早くなりスクレイピング時間が短縮できる。
  • プロキシの帯域幅が節約できて低コストなスクレイピングが実現できる。

どうやってやるのか?

今回は例として Zennのトップページ で実践してみます。(技術的な解説なのでどうかお許しを🙏)

工夫をせずにスクレイピングすると、840KBのデータを受け取ることになります。

画像とWebフォントを無効化しよう

const browser = await chromium.launch({
  args: ["--blink-settings=imagesEnabled=false", "--disable-remote-fonts"]
});

Playwrightでは上記のargsオプションにて --blink-settings=imagesEnabled=false で画像を無効化でき、 --disable-remote-fonts でWebフォントを無効化できます。

この2つを無効にするだけでもかなり効果的です。

しかし、スクレイピングに関係ない箇所のCSSやJSが邪魔です。
もっと削減してみましょう!

CSSやJSを無効化する(逆にファイル名を指定して許可もできる)

import { chrome } from "@utils/functions/browser";

(async () => {
  const { page } = await chrome();

  await page.route(/(\.css.*|\.js.*|google|logo)/, (route) => {
    return route.abort();
  });

  await page.goto("https://zenn.dev/");
})();

※上記の記述は こちらの記事 を踏襲した形で書いています。気軽にスクレイピングしやすくなりますのでオススメの書き方です!

このようにして実行してみましょう。
.route() は指定した条件にヒットしたレスポンスリソースに対して、 route.abort() でリクエストを投げないということができます。逆にリクエストを投げたい場合は route.continue() とすると特定のファイルだけリクエストを投げることもできます。

このようにCSSやJSが全て無効化されています。
その結果、わずか20.8KBまでに減らすことができています。

Next.jsのSSRで作られたサイトはJSを動かさなくてもスクレイピングできてしまう

ZennはNext.jsで作られているため、JSが動かないとスクレイピングができないと思われるかもしれません。しかし、実はNext.jsのSSRで作られたサイトはかなりスクレイピングしやすいです。

しかし、実はNext.jsのSSRで作られたサイトは低コストで尚且つスクレイピングがしやすいサイトです。

Next.jsのSSRはHTMLソースに #__NEXT_DATA__ のscriptタグにpropsデータが含まれています。propsはSSRで処理された動的に挿入予定のデータが格納されています。つまり、ここの値を取得できてしまえば、実質的にクソでかJSONという扱いでスクレイピングできます。

ちょっと実践してみましょう。

import { chrome } from "@utils/functions/browser";

(async () => {
  const { page } = await chrome();

  await page.route(/(\.css.*|\.js.*|google|logo)/, (route) => {
    return route.abort();
  });

  await page.goto("https://zenn.dev/");

  const nextData = await page.locator("#__NEXT_DATA__").innerText();
  const props = JSON.parse(nextData);

  console.log(props);
})();

取得した値を JSON.parse でパースすると、

dailyTechArticlesdailyBooks などZennのコンテンツが取得できていることが分かります。あとはこのデータを元に加工してあげればいいだけなのです。

※ただし、クライアントサイドでデータフェッチしている場合はPlaywrightの力を借りることになりますね!軽量化するならば不要なJSを狙い撃ちしながらデータ量を削減していく必要があります。

まとめ

Playwrightを使えばどんなサイトも軽量化しながらスクレイピングできます。
node-fetch + Cheerioのスクレイピングにこだわらず、Playwrightもガンガン使っていくと良いでしょう!

Playwrightは魔法である!

Discussion