🤖

[Playwight] TSで動的サイトをスクレイピング

2024/08/03に公開

はじめに

動的なサイトとは、JSによるDOMの更新が行われるサイト。
静的なサイトと違って、URLを指定してHTMLを取得し、スクレイピングをしようとしてもうまくいかない。(その時点ではJSにより生成される本体のコードを取得できていないから)
よって、Playwrightpuppeteerseleniumなどのヘッドレスブラウザを操作するライブラリを使って実際にサイトにアクセスすることで、DOM操作後のHTMLが取得できます。
今回は、

  • シンプル
  • 動作の軽さ
    の点から、Playwrightを使用します。

動作環境

  • mac: System Version: macOS 14.4 (23E214)
  • node: v22.2.0
  • Playwright: 1.45.3

手順

前提

以下の動的なサイトをスクレイピングします。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Example of Dynamic content for html</title>
</head>
<body>
    <div id="content">Loading...</div>
    <script>
        document.addEventListener("DOMContentLoaded",(event)=>{
            setTimeout(()=>{
                document.getElementById("content").innerHTML="Hello World!";
            },1000);
        })
    </script>
</body>
</html>
  1. アクセスすると、Loading...と表示される
  2. 1秒後にHello World!に変わる
  • axioscheerioなどを使ってHTMLを取得するだけなら、Loading...しか取得できない

Playwrightを使ってHello World!を取得する

% npm install playwright -D
playwright.ts
import { chromium } from 'playwright';

(async () => {
    const url = "http://127.0.0.1:5500/index.html";
    const browser = await chromium.launch();
    const page = await browser.newPage();

    await page.goto(url);
    await page.waitForSelector("#content");

    await page.waitForFunction(
        selector => document.querySelector(selector)?.innerHTML !== "Loading...",
        '#content'
    )
    const content = await page.$eval("#content", el => el.innerHTML);
    console.log("content", content);
    await browser.close();
})();
  1. ヘッドレスChromiumを起動
  2. URL先に移動
  3. #contentが読み込まれるまで待機
  4. #contentがLoading...(最初の文字列)から変更されるまで待機
  5. #contentの中身を取得
% npm run dev

> type_scraping@1.0.0 dev
> ts-node ./src/playwright.ts

content:  Hello World!
GitHubで編集を提案

Discussion