Node.jsでWebスクレイピングを実現できる4つのツール

April 29, 2020
執筆者
Sam Agnew
Twilion
レビュー担当者

4 tools for web scraping in Node.js - JP

この記事はデベロッパーエバンジェリストのSam Agnewが執筆した記事(英語)を日本語化したものです。

必要なデータをオンラインで入手できる場合でも、専用のREST APIが使えるとは限りません。幸いなことに、JavaScript開発者向けにNode.js用のさまざまなツールが用意されており、Webサイトから直接データのスクレイピングと解析を行い、プロジェクトやアプリケーションで使用できます。

ここでは、4つのライブラリーを取り上げ、その機能とともにそれぞれを比較して説明します。

コンピューターに最新バージョンのNode.js(16.14.2以上)とnpmがインストールされていることを確認してください。ターミナルを開き、コードを格納するディレクトリで次のコマンドを実行します。

npm init --yes

本稿で構築する一部のアプリケーションにではGotライブラリーを使用してHTTPリクエストを行うため、次のコマンドを使用して同じディレクトリにインストールします。

npm install got

こちらのWebページにあるVideo Game Music Archiveには、さまざまなNintendoゲームミュージックが収録されています。ここから固有のMIDIファイルを参照するリンクを探す処理を今回ご紹介するライブラリーのサンプル課題として使用します。

Midi files

Webスクレイピングのためのヒント

各ツールを紹介する前に、どの方法を選ぶ場合でも役に立つ共通のテーマがあります。

必要なコンテンツを解析するコードを作成するには、一般的にブラウザが表示するHTMLを参照する必要があります。Webページはそれぞれに異なるため、適切なデータを取得するには少し工夫を凝らし、パターン認識などを試みることになります。

最新のほとんどのブラウザは、開発者用の便利なツールが利用できます。対象の要素を右クリックするだけで、その要素のHTMLを詳しく調べることができます。

investigate elements

フィルターを使用して特定のコンテンツに絞り込む必要があることもよくあります。これを行うため、このチュートリアルのコード例にも見られるCSS選択機能を使用して特定の条件に該当するHTML要素を収集します。正規表現もWebスクレイピングに多く利用されるツールです。これに加え、さらに細分化の必要があれば、要素の内容をフィルターする関数を作成できます。例えば次のように、ハイパーリンクがMIDIファイルを参照しているかどうかを判別できます。

const isMidi = (link) => { 
    // Return false if there is no href attribute. 
    if(typeof link.href === 'undefined') { return false } 

    return link.href.includes('.mid'); 
};

多くのWebサイトでは、サービス利用規約により、Webスクレイピングが禁止されていることにも注意が必要です。この点を必ず確認するようにしてください。以上のことを頭に入れ、本題に入りましょう!

jsdom

jsdomはNode.js用の多くのWeb標準を実装した純粋なJavaScriptライブラリです。Webアプリケーションのテストとスクレイピングに役立つツールです。インストールするには、ターミナルで次のコマンドを実行します。

npm install jsdom

次のコードで、前述したVideo Game Music ArchiveページにあるMIDIファイルのリンクを、すべて収集します。

const got = require('got'); 
const jsdom = require("jsdom"); 
const { JSDOM } = jsdom; 

const vgmUrl= 'https://www.vgmusic.com/music/console/nintendo/nes'; 

const isMidi = (link) => { 
  // Return false if there is no href attribute. 
  if(typeof link.href === 'undefined') { return false } 

  return link.href.includes('.mid'); 
}; 

const noParens = (link) => { 
  // Regular expression to determine if the text has parentheses. 
  const parensRegex = /^((?!\().)*$/; 
  return parensRegex.test(link.textContent); 
}; 

(async () => { 
  const response = await got(vgmUrl); 
  const dom = new JSDOM(response.body); 

  // Create an Array out of the HTML Elements for filtering using spread syntax. 
  const nodeList = [...dom.window.document.querySelectorAll('a')]; 

  nodeList.filter(isMidi).filter(noParens).forEach(link => { 
    console.log(link.href); 
  }); 
})();

非常にシンプルなクエリセレクタaを使用してページ上にあるすべてのハイパーリンクにアクセスし、いくつかの関数によりコンテンツをフィルタリングし、必要なMIDIファイルのみを取得しています。noParens()フィルター関数は、正規表現をもとに括弧を含むMIDIファイル(同じ曲の別バージョン)を除外します。

コードをindex.jsファイルに保存し、ターミナルでnode index.jsコマンドを実行します。

このライブラリーのさらに詳しい説明についてはjsdomの使用方法を説明したチュートリアルで取り上げています。

Cheerio

Cheerioはjsdomと同様のライブラリーですが、より軽量で高速に実行が可能です。jQueryコアのサブセットが実装され、多くのJavaScript開発者が慣れ親しんだAPIが提供されます。

インストールするには、次のコマンドを実行します。

npm install cheerio

先ほどと同様のタスクを実行するために必要なコードは非常に似ています。

const cheerio = require('cheerio');
const got = require('got');

const vgmUrl= 'https://www.vgmusic.com/music/console/nintendo/nes';

const isMidi = (i, link) => {
  // Return false if there is no href attribute.
  if(typeof link.attribs.href === 'undefined') { return false }

  return link.attribs.href.includes('.mid');
};

const noParens = (i, link) => {
  // Regular expression to determine if the text has parentheses.
  const parensRegex = /^((?!\().)*$/;
  return parensRegex.test(link.children[0].data);
};

(async () => {
  const response = await got(vgmUrl);
  const $ = cheerio.load(response.body);

  $('a').filter(isMidi).filter(noParens).each((i, link) => {
    const href = link.attribs.href;
    console.log(href);
  });
})();

コンテンツのフィルタリングを行う関数は、CheerioのAPIに組み込まれているので、コードを追加して要素コレクションを配列に変換する必要はありません。index.js内のコードを、上記のコードに置き換えて再度実行します。Cheerioは軽量なライブラリーのため、かなり高速で実行されます。

さらに詳しい説明は、こちらのCheerioの使用方法のチュートリアルで取り上げています。

Puppeteer

Puppeteerが前述のライブラリ2つと大きく異なる点は、ヘッドレスのブラウザスクリプティング用ライブラリーであることです。DevToolsプロトコルを介してChrome/Chromiumをコントロールする、ハイレベルなAPIが提供されます。固定データの読み取りだけでなく、Webアプリケーションと対話して操作するコードを作成できるため、より多目的に使えます。

インストールするには、次のコマンドを実行します。

npm install puppeteer

Puppeteerを使用したWebスクレイピングは先の2つと大きく異なります。URLからHTMLコードを取得しオブジェクトに提供するコードではなく、与えられたURLのHTMLを処理し、実際のドキュメントオブジェクトモデルが構築されたブラウザコンテキスト上で動作するコードを記述することになります。

次のコード例は、Puppeteerのブラウザに目的のURLを参照させ、先に解析したものと同じハイパーリンク要素すべてにアクセスするよう指示しています。

const puppeteer = require('puppeteer');

const vgmUrl = 'https://www.vgmusic.com/music/console/nintendo/nes';

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.goto(vgmUrl);

  const links = await page.$$eval('a', elements => elements.filter(element => {
    const parensRegex = /^((?!\().)*$/;
    return element.href.includes('.mid') && parensRegex.test(element.textContent);
  }).map(element => element.href));

  links.forEach(link => console.log(link));

  await browser.close();
})();

ページのリンクに対してフィルタリングを行うために、いくつかのロジックを作成していますが、フィルター関数をさらに宣言するのではなく、インラインで処理しています。ブラウザに何をすべきかを指示するサンプルコードもありますが、別のNodeモジュールを使用して、スクレイピングしようとしているWebサイトにリクエストを送る必要はありません。Puppeteerはこのような単純な処理でも他のツールに比べて非常に時間がかかりますが、コンテンツが静的でないページを扱う場合には大変便利なツールです。

その他のPuppeteerの機能を利用して動的なWebアプリケーションを操作する方法の詳細は、Puppeteerの使用法を詳しく説明したチュートリアルで取り上げています。

Playwright

Playwrightも、ヘッドレスのブラウザスクリプティング用のライブラリーであり、Puppeteerと同じ開発チームが作成したものです。APIと機能はPuppeteerとほぼ同じですが、異なるブラウザでの使用を想定してあり、Chrome/ChromiumのほかにFireFoxやWebkitでも使用できます。

インストールするには、次のコマンドを実行します。

npm install playwright

今回のタスクを実行するPlaywrightのコードは前述のライブラリとほぼ同じですが、使用するブラウザを明示的に宣言する必要があります。

const playwright = require('playwright');

const vgmUrl = 'https://www.vgmusic.com/music/console/nintendo/nes';

(async () => {
  const browser = await playwright.chromium.launch();
  const page = await browser.newPage();

  await page.goto(vgmUrl);

  const links = await page.$$eval('a', elements => elements.filter(element => {
    const parensRegex = /^((?!\().)*$/;
    return element.href.includes('.mid') && parensRegex.test(element.textContent);
  }).map(element => element.href));

  links.forEach(link => console.log(link));

  await browser.close();
})();

Puppeteerで紹介したコードと同じ機能が、同じように実行されます。Playwrightのメリットは、複数種類のブラウザで使用できる多用途性にあります。このコードを他のブラウザで実行し、どのように機能するか試してみてください。

他のライブラリーと同じく、Playwrightの使用法を紹介したチュートリアルで詳しく取り上げています。

広大なWorld Wide Webを活用するために

これでプログラムを使用してWebページのコンテンツを収集できるようになり、膨大なデータにアクセスし、あらゆるプロジェクトのニーズに応えることができます。ただし、1つ注意すべき点があります。WebページのHTMLが変更されるとコードが機能しなくなることがあるため、スクレイピングを活用するアプリケーションの作成では、すべての要素を最新の状態にしておく必要があります。

皆さんが何を構築されるか、とても楽しみです。体験談の共有やご質問など、どうぞお気軽にお問い合わせください。