Puppeteerでウェブサイトをスクレイピング

PhantomJSが消えた今(クラウドサービスは生きてる)、ウェブの情報スクレイピングする手段としては、SeleniumとPuppeteerくらいかなぁというのも、最近のウェブは動的に情報を生成しているので、例えばGoogle Apps Scriptで拾いに行っても、JavaScript実行前の骨組みしか取得できないケースがあったりします。

しかし、PuppeteerはただChromeの操作を自動化するだけじゃなく、ウェブスクレイピングでも力を発揮します。ということで、今回世間を騒がせているマスク情報を手に入れるべく、対象のページの商品情報を取得してみたいと思います。実際には、このコードに更にIFTTTのWebhooksを使ってLINEに通知したり、TeamsのIncoming Webhookを叩いたり、また「アイマスク」などのノイズ情報も除去したり、Node-Cronで定期的に自動実行させてやる必要があるので、こんな事も出来るよってサンプルです。

今回使用するモジュール

今回はスクレイピングさせる為の補助として、cheerioと呼ばれるNode.js上でjQueryのようにDOM操作をする事のできるモジュールを入れています。

また、macOS用に作っているので、executablePath/Applications/Google Chrome.app/Contents/MacOS/Google Chromeとなります。

※スクレイピングは相手のサーバに負荷を掛ける手法です。よって、非常に短いスパンで連続アクセスするような行為はNGです。

事前準備

セレクターの取得

今回のPuppeteerは自動操作というよりは、ウェブスクレイピングの処理なのでほぼほぼNode.jsでコーディングする形になります。しかし、ページのどこの部分をスクレイピングするか?ということで、はじめにセレクターを渡してあげる必要があります(でないと全体取得になってしまう)

このセレクタですが、Chromeデベロッパーツールで簡単に取得が可能です。以下の手順でページの対象部分のセレクタを取得しておきます。

  1. Chromeで対象のページを表示する
  2. 商品群の情報が出てるエリアを右クリック⇒検証でデベロッパーツールが起動
  3. 商品エリアの部分が今回の取得対象となる部分。
  4. 3.を右クリック⇒Copy⇒Copy Selectorを選択
  5. 今回のコードのselector変数に入れる。これで完了。

図:セレクタの取得は意外と簡単

モジュールのインストール

これはとても簡単。

  1. フォルダを作成し、npm init -yを実行
  2. 以下のコマンドでモジュールをインストール
  3. index.jsを作成して、node index.jsで実行。これだけ
npm install cheerio
npm i puppeteer

ソースコード

スクレイピングするコード

"use strict"

const puppeteer = require('puppeteer-core');
const path = require("path");
const cheerio = require('cheerio');

//Chromeのパスを取得
var chromepath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";

//puppeteer発動
main();

//ブラウザ操作メイン関数
async function main() {
  const browser = await puppeteer.launch({
    headless: false,
    executablePath: chromepath,
    ignoreDefaultArgs: ["--guest",'--disable-extensions','--start-fullscreen','--incognito',],
    slowMo:200,
  });

  //pageを定義
  const page = await browser.newPage()
  const navigationPromise = page.waitForNavigation()

  //対象のウェブサイトを開く
  var url = "ここに対象のURLを入れる"
  await page.goto(url, { waitUntil: 'domcontentloaded' }); // domを読み込むまで待機
  await page.setViewport({ width: 1536, height: 714 })
  await navigationPromise

  //メイン部分を取得(Chrome Dev Toolでセレクターをそのまんま貼り付け)
  var selector = "ここに対象の取得しておいたセレクターを入れる";

  //HTMLデータを取得
  var html = await page.$eval(selector, item => {
        return item.innerHTML;
  });

  //cheerioに食わせる
  const $ = await cheerio.load(html)

  //データ用配列
  var amaarray = [];

  //data-indexの値を拾っていく
  await $('.s-result-item').each((index, element) => {
      //配列を用意する
      var dataman = {};

      // 指定の要素内のdata-index属性の値を取得する
      const dataindex = $(element).data("index");
      dataman.dataindex = dataindex;

      //商品の価格を取得
      const itemPrice = $(element).find('商品価格のClassを指定').text();
      dataman.itemPrice = itemPrice;

      //品名を取得
      const itemName = $(element).find('商品名が入ってるClassを指定').text();
      dataman.itemName = itemName;

      //商品のURL取得してみたり
      const itemLink = $(element).find('URLが入ってるclassを指定').attr('href');
      let amaurl = "ここにドメイン名を入れる" + itemLink;
      dataman.itemLink = amaurl;

      //スターを取得する
      const itemStar = $(element).find('スター情報のClassを指定').text();
      dataman.itemStar = itemStar;

      //ポイントを取得する
      const itemPoint = $(element).find('ポイント情報のclassを指定').children('span').text();
      dataman.itemPoint = itemPoint;

      //配列にぶっこむ
      amaarray.push(dataman);

  });
  
  //データを出力する
  await console.log(amaarray)

  //ブラウザを閉じる
  await browser.close()

}
  • 今回はmacOS上で動かしてるので、executablePathは異なります。
  • ロードするURLは検索結果画面からのスタートになります。
  • selector変数には前項の事前準備で取得した対象のセレクタを指定します。
  • html変数にawait page.$evalにてまずは、セレクター内のHTMLを取得させておきます。
  • 取得したHTMLはそのままでは扱いが面倒なので、cheerioに渡してjQuery風にDOM操作出来るようにしておきます。
  • 最初のクラス以下をforEachで回して各商品の情報を取得させていきますので、配列等を用意しておく
  • その中に子要素や孫要素が入ってるのですが1個ずつ掘っていくのは骨が折れるので、class指定でfindで探させます
  • URLの場合には、hrefをattrにて取得するようにします。
  • これらの情報は用意しておいた{}にkey名と共に突っ込んでいく
  • 最後に配列に情報の塊をぶっこむ。
  • 相手のウェブの仕様が変われば作り直しになるのは、ウェブスクレイピングの宿命

関連リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)