🦀

【令和最新版】令和のWebスクレイピング(クロール)【ベストプラクティス】

2024/07/29に公開

こんにちは、株式会社FP16で結構コードを書いている二宮です。

最近Webスクレイピングのコードを色々な方法で書いているので、そこで得た知見をここに残しておこうと思います。
ほぼ毎日なにかのWebスクレイピングコードを書いています。

https://fp16.co.jp/scraping/

Webスクレイピング手段

Webスクレイピングには色々な方法があります。
私が最近主に使っているのはこの5つの手段です。

  1. cheerioでHTMLを解析
  2. Playwrightなどで要素指定でデータを取得する
  3. APIを見つけて叩く(バックエンドとの通信を再現してデータを取得)
  4. LLMでサイト構造を解析してデータを取得する
  5. Next.jsからのレスポンスに含まれているデータを解析して取得する

これが令和のWebスクレイピングのベストプラクティスだと思っています。

これらの方法を、目標に合わせて使い分けています。

使い分け方

CheerioでHTML解析

JavaScriptを使った遅延読み込みなどがなく、完成したHTMLが返ってくるサイトをスクレイピングする場合はCheerioが最適です。
これはなんとCloudflare Workerで動くので、大量にスクレイピングする場合もサーバー代が0円で住んだりします。
https://github.com/cheeriojs/cheerio

Playwright

JavaScriptでのデータ読み込みなどがある場合はPlaywrightを使ってスクレイピングする必要があります。
一般的なブログ形式のウェブサイトや、テンプレートで型が決まっていて複数の商品データなどを取得したい場合、かつ情報の取得がフロントからAPIで行われていない場合
Seleniumなど色々ブラウザを操作するツールはありますが、今からやるならPlaywrightをおすすめします。

https://playwright.dev/

APIを叩く

フロントからAPI経由でデータの取得が行われていた場合は間違いなくこれが一番です。
要素指定などではないので、取得に失敗する確率が圧倒的に減ります。
Chromeでウェブサイトを開き、Networkタブから通信情報を確認します。
RESTやGraphQLでの通信の場合にこの方法を使っています。
そして取得する数などのプロパティを調整してデータを取得します。

LLMでサイト構造を解析

クロール対象となるウェブサイトが多く、構造も複雑な場合にLLMでサイト構造解析を行っています。
これはCheerioやPlaywrightとLangChainを組み合わせて使います。
静的なHTMLが返ってくる場合はCheerioでデータを<main>タグなどに絞りLLMに渡します。
動的な場合はPlaywrightでアクセスし、生成後のHTMLを<main>タグなどに絞ってLLMに渡します。

FP16では社内にGPUサーバーを設置し、Ollamaでモデルをホスティングしています。
https://ollama.com/

https://zenn.dev/fp16/articles/e8c61e2f62e6b6

Next.jsからのレスポンスデータを解析

Next.jsで作られているサイトが増えていますが、サーバーサイドでデータを取得していて、フロントには表示していない場合などもあります。(未ログイン時)
その場合はNext.jsがレスポンスに含むself.__next_f.pushを解析すると取得できる場合があります。
例えばこのように解析します。

const dom = new JSDOM(html);
const scripts = dom.window.document.querySelectorAll("script");

function getTransactionDate(scripts) {
  let transactionDate = null;

  for (let i = 0; i < scripts.length; i++) {
    const script = scripts[i];
    const text = script.textContent;
    if (text?.includes("self.__next_f.push")) {
      const jsonLikeString = text.match(
        /self\.__next_f\.push\(\[1,"(.*?)"\]\)/,
      );
      if (jsonLikeString?.[1]) {
        const jsonString = `{"data": "${jsonLikeString[1]}"}`.replace(
          /\n/g,
          "\\n",
        );

        try {
          const json = JSON.parse(jsonString);
          const dataString = json.data;

          const dateMatch = dataString.match(
            /"dateLastSold":"(\d{4}-\d{2}-\d{2})"/,
          );
          if (dateMatch?.[1]) {
            transactionDate = dateMatch[1];
            break;
          }
        } catch (e) {
          console.error("Failed to parse JSON string", e);
        }
      }
    }
  }

  return transactionDate;
}

こんなサービスもあるよ

Automa

Chrome拡張型のブラウザ自動操作・データ取得アプリです。
完全にシステムに組み込む必要がなく、簡単にデータ取得を行いたい場合はAutomaで十分かもしれません。
https://www.automa.site/

Firecrawl

LLMにRAGとかで最新のデータを渡すためにスクレイピングしたいならこれを使うのが最近は多い。
セルフホスティングも可能!
https://www.firecrawl.dev/

おしまい

最後まで読んでいただきありがとうございました!
今後各方法の実際の実装方法の記事を書けたらいいなと思っています。
たくさんいいねもらえると嬉しいです!

株式会社FP16

Discussion