crawleeでクローラを作る

最近Typescript/Playwright を利用しているので、 Playwrightを利用すると簡単にクローラが作れそう。

というところから技術調査をしたところcrawleeが見つかったので試しに利用してクローラをつくってみた話。

以下に手順を記す。

プロジェクトの作成

https://crawlee.dev/docs/introduction/setting-up

setupを参考に

npx crawlee create my-crawler

を実行する。

crawleeがインストールされていないと

Need to install the following packages:
  crawlee@3.3.1
Ok to proceed? (y)

のようにcrawleeをインストールするか聞いてくるので y でインストールする。

その後

? Please select the template for your new Crawlee project
  Getting started example [TypeScript]
  Getting started example [JavaScript]
  CheerioCrawler template project [TypeScript]
❯ PlaywrightCrawler template project [TypeScript]
  PuppeteerCrawler template project [TypeScript]
  CheerioCrawler template project [JavaScript]
  PlaywrightCrawler template project [JavaScript]

のように、どのテンプレートを利用するか聞いてくるので、上下キーで PlaywrightCrawler template project [TypeScript] を選択する。

これで、 my-crawler ディレクトリにテンプレートが作成される。

クローラの作成

クローラ本体は、 src/routes.ts に登録していく。

ファイルを見ると addDefaultHandleraddHandler 'detail' がすでに登録されている。

defaultHandler

DefaultHandlerの中身が、クローラの開始時に呼ばれる。

router.addDefaultHandler(async ({ enqueueLinks, log }) => {
    log.info(`enqueueing new URLs`);
    await enqueueLinks({
        globs: ['https://crawlee.dev/**'],
        label: 'detail',
    });
});

globs, label の組みで、 実行するクローラを変えられる。 globにマッチしたURLはこのlabelでキューイングされ、クローリング対象となる。

enqueueLinksすると、現在のページの linkを全部見てくれるので、自分でリンクを探してキューイングしなくてよいのがとても楽。

ただし、動的ページで、後でlinkが生えてくるような場合は、 playwrightを利用して、linkが生えてくるのを待ってからenqueuLinksする必要がある。

たとえば

  await page.locator("#page-1").waitFor({ state: "attached" });

などで 読み込みをまったりしている。

別のクローラを利用したければ、 enqueueLinksを複数書いて、それぞれURLパターンを登録していく。

handler for label

router.addHandler('detail', async ({ request, page, log }) => {
    const title = await page.title();
    log.info(`${title}`, { url: request.loadedUrl });

    await Dataset.pushData({
        url: request.loadedUrl,
        title,
    });
});

こちらが、 enqueueLinksで label: 'detail' としてキューイングしたURLが渡ってくるクローラ本体。

この中で好きにスクレイピングするコードを書くと良い。

もちろん、この中でも enqueueLinksでキューイングできる。 何度もキューイングしても、同一URLは2度はクローリングしないように制御されているので安心。

好きなlabelでenqueueLinksして、 addHandlerでlabelを指定してあげれば、いくらでもnestしたり、URLタイプごとに処理を分けたりできる。

開始URLの設定

src/main.ts に startsUrlsという配列が定義されているので、 そこにクローリング対象のトップURLを並べればよい。 コマンドライン引数からURLを組み立てるなどすると、汎用性が上がる。

実行

npm run start:dev

で実行できる。コンパイルしたものを利用したい場合は

npm run build

でビルドしておき、

npm run start:prod

でビルドしたものを実行できる。

終わりに

playwrightを利用すればseleniumのようにweb driverとbrowserのバージョン不一致問題など、面倒ごとなくクローラが作成できると思っていたら、crawleeというとても便利なライブラリが見つかり、

とても簡単にクローラが書けることがわかった。

わずか20行ほどコードを書くだけでクローリングできて、とても楽だった。

今後はメモリに余裕のある環境では積極的にcrawleeを使っていきたい。