Webサイトから情報を取得する③ スクレイピング

スクレイピングはウェブサイトから情報を自動的に収集できる便利な技術です。
たとえば、商品価格や在庫状況の自動取得、ニュース記事やブログ記事の自動取得といった目的で、多方面で用いられています。
この便利なスクレイピング技術ですが、GASを使っても実現可能なんです。
しかも、GASを使うメリットとしては、
1. 環境構築が簡単(不要)
2. 定期実行が簡単
3. 取得結果の保存・共有も簡単
と、3つの簡単を挙げることができます。

ただ、スクレイピングを行う際には、いくつかの注意点もありますので、それらも含めてこの記事で学んでいきましょう!

対象サイト

今回、スプレイピングのサンプルとして対象とするのは、シリーズ①からの繋がりで、東京都練馬区のホームページから、練馬区の世帯と人口の数値を取得してみたいと思います。

ということで、シリーズ①で準備したスプレッドシート「情報取得アプリ」の「URL」シートに対象サイトのURLを追加します。

ついでにここで、「練馬区人口」という新しいシートも用意しておきましょう。

スクレイピングの注意点

  1. 利用規約を確認しよう
    対象とするサイトがスクレイピングを禁止している場合がありますので、利用規約を必ず確認しましょう。
    練馬区ホームページの場合、こちらのサイトポリシーを確認すると、スクレイピングについては特に禁止はされていないようです。
  2. 過度なスクレイピングは控えよう
    利用規約で禁止されてはいないといっても、モラルとして、対象サイトのパフォーマンスに悪影響を与えるような過度なスクレイピングは控えましょう。
    サイト運営者や他の利用者に損害を与えるような迷惑行為は、法的責任を問われることもありますので、十分に気を付けてください。

    今回のスクリプトでは、月1回サイトにアクセスして、練馬区の世帯数と人口のデータを取得する設計となっています。

Parserライブラリの導入

GASでのスクレイピングをより簡易にしてくれるParserライブラリを前もって導入しておきます。

エディタの左メニューから「ライブラリ」の➕ボタンを押します。
スクリプトID1Mc8BthYthXx6CoIz90-JiSzSafVnT6U3t0z_W3hLTAX5ek4w0G_EIrNw
をコピペして「検索」し、「Parser」と表示されたら「追加」します。

ライブラリにParserが追加されました。
これでParserクラスが利用可能です。

コード①:スクレイピング・パート

コードの全体構成としては2つのパートから成っており、1つはサイトから情報を取得してくるスクレイピング・パート、もう1つは取得した情報をスプレッドシートに展開するパートです。

では、スクレイピング・パートのwebScraping関数から見て行きましょう。

function webScraping() {
  const sheet_url = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('URL');
  const url = sheet_url.getRange(3, 2).getValue();
  const response = UrlFetchApp.fetch(url);
  const content = response.getContentText('UTF-8');

 // html要素の抽出
  const parser = Parser.data(content);
  const households = parser.from('<td style="width: 32.4%" class="right bottom">').to('</td>').build();
  const data = parser.from('<td class="right bottom">').to('</td>').iterate();
  const [men, women, total] = data;
  
  // 取得年月
  let date = new Date();
  date = Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy年M月');

  return [date, households, men, women, total];
}

2〜5行目のコードはシリーズ①を参考にしてください。
ここで最も中心となるのは、html要素を抽出する8〜10行目のコードです。
解説していきます。

Parser.data(content)

インストールしたParserライブラリをここで使います。
Parserクラスのdata( )メソッドにcontentを渡すことでhtml要素の切り出しが容易になります。
具体的には、次のfrom( )とto( )メソッドが使えるようになります。

parser.from('要素の前タグ’).to('要素の後ろタグ’).build( )

html要素のタグを知るには、Chromブラウザではデベロッパーツールを使います。
抽出したい要素(ここでは世帯数の388,534)の上で右クリックし、メニューから「検証」を選択します。

デベロッパーツールが表示され、該当タグが水色でハイライトされています。

ハイライトの上で右クリックし、メニューから「要素をコピー」します。
「388,534」の前部分をfrom( )に、後ろ部分をto( )にペーストします。
シングルクォーテーションで囲うことを忘れずに。

build( )該当する要素の先頭のみ抽出
iterate( )該当する要素をすべて抽出
戻り値は配列

戻り値の配列を変数dataに格納し、分割代入で各変数に代入しました。

配列については↓の記事が参考になります。

分割代入については↓の記事が参考になります。

Utilities.formatDate(date, 'Asia/Tokyo’, 'yyyy年M月’)

スプレッドシートの記録用にDateオブジェクトを用意し、formatDate( )で取得年月を成形。
最後に、date, households, men, women, totalを配列でreturnします。

webScraping関数のテスト

では、webScraping関数を実行して、ログ出力してみましょう。

取得年月、世帯数、人口(男)、人口(女)、人口(計)の数値がしっかりと取得できています。
スクレイピング成功です!

コード②:スプレッドシート・パート

つづいて、webScraping関数で取得したデータをスプレッドシートに展開、保存するパートdataToSS関数を見ていきましょう。
とは言っても簡単です。

function dataToSS() {
  const results = webScraping();
  const sheet_pop = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('練馬区人口');
  const lastRow = sheet_pop.getLastRow();
  sheet_pop.getRange(lastRow + 1, 1, 1, 5).setValues([results]);
}

関数内でwebScraping関数を実行して、結果を変数resultsに格納します。
あとは、シート「練馬区人口」でgetRangeして、setValuesにresultsを渡せばよいだけです。
スプレッドシートに展開させるためには二次元配列である必要があるので、配列のresultsをさらに[ ]で括って二次元配列化しています。

完成コード

function webScraping() {
  const sheet_url = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('URL');
  const url = sheet_url.getRange(3, 2).getValue();
  const response = UrlFetchApp.fetch(url);
  const content = response.getContentText('UTF-8');

 // html要素の抽出
  const parser = Parser.data(content);
  const households = parser.from('<td style="width: 32.4%" class="right bottom">').to('</td>').build();
  const data = parser.from('<td class="right bottom">').to('</td>').iterate();
  const [men, women, total] = data;
  
  // 取得年月
  let date = new Date();
  date = Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy年M月');

  return [date, households, men, women, total];
}

function dataToSS() {
  const results = webScraping();
  const sheet_pop = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('練馬区人口');
  const lastRow = sheet_pop.getLastRow();
  sheet_pop.getRange(lastRow + 1, 1, 1, 5).setValues([results]);
}

それでは、dataToSS関数を実行してみると、、、

無事、「練馬区人口」シートに展開できました!

トリガーの設定

あとは、dataToSS関数を毎月1回定期実行するようトリガーを設定すれば完成です。

「時間主導型」
「月ベースのタイマー」を選択します。
日付と時刻はお好みでどうぞ。

トリガー設定の基本はこちらで。

これで、毎月の練馬区の世帯数と人口のデータを自動でスクレイピングして保存してくれます。
便利ですね!

GASを使うメリット
1. 環境構築が簡単(不要)
2. 定期実行が簡単
3. 取得結果の保存・共有も簡単

3つの簡単がお分かりいただけたのではないでしょうか!

シリーズ目次:Webサイトから情報を取得する

  1. Webサイトから情報を取得する① ファイルのダウンロード
  2. Webサイトから情報を取得する② スプレッドシートにインポート
  3. Webサイトから情報を取得する③ スクレイピング
  4. Webサイトから情報を取得する④ Web API vol.1
  5. Webサイトから情報を取得する⑤ Web API vol.2