読者です 読者をやめる 読者になる 読者になる

GoogleAppsScriptでアルクマスケジュールをスクレイピングし、地図に表示してみた

GoogleAppsScript

NSEGアドベントカレンダーに参加して思い出したのですが、以前別のアドベントカレンダーアルクマを追いかけていたことがありました*1*2

 
それらではGoogleのいくつかのサービスと組み合わせたものの、Google Apps Script(以下、GAS)だけを使って追いかけてはいませんでした。

そこで、今回思い出したついでに、GASだけでアルクマカレンダーをスクレイピングして地図に表示してみました。

 

処理の流れ

流れは

  1. Chromeなどのブラウザで、GASで作ったWebページのエントリポイントへアクセス
  2. GASで、アルクマスケジュールのサイトをスクレイピングし、日時・イベント名・場所を取得
  3. GASで、場所とMaps.newGeocoder().geocode()を使って、緯度・経度を取得
  4. GASで、緯度・経度とMaps.newStaticMap()を使って、地図画像を取得
  5. GASで、地図画像を含むHTMLをブラウザに返す

のような感じにします。

 
なお、GASで実装する場合、

  • 使えるGoogle Mapsは、Static Mapのみ
  • HTMLにはいろいろと制限あり
    • ブラウザでソースコードを確認すると読めない感じのソースになっていたり
  • Geocoding APIには一日の使用回数制限あり

の制限などがあるものの、今回の目的は達成できそうでした。

 

実装

Webからのエントリポイント

doGet()にて、用意したHTMLテンプレートを返します。

なお、前回以降に変更が入ったようで、現在では.setSandboxMode(HtmlService.SandboxMode.IFRAME)が必要です。
HTML Service: Create and Serve HTML  |  Apps Script  |  Google Developers

function doGet(){
  var output = HtmlService.createTemplateFromFile("index");
  return output.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME);
}

 
用意したHTMLテンプレートは以下のような感じで、その中で、

  • getEvents()
  • getMap()

の2つの自作のGAS関数を呼び出しています。

また、CSS3のFlexboxも使えるようだったので、<style>にて使っています。
これからのCSSレイアウトはFlexboxで決まり! | Webクリエイターボックス

なお、以下のBest Practicesに従い、不要な<html><head><body>などのタグは削除しました。見た感じ、確かに削除しても正常に動作しています。
HTML Service: Best Practices  |  Apps Script  |  Google Developers

<!DOCTYPE html>

<title>アルクマップ - GAS版</title>
<style>
.day {
  color: blue;
}
.event {
  margin-bottom: 10px;
}
.block{
  display: flex;
}
</style>
<div id="info" class="block">
  <div id="schedule">
    <h3>アルクマスケジュール</h3>
      <!-- GASのgetEvents()を呼び出して、戻り値をもとにHTMLを組み立てる -->
      <?
         var events = getEvents();
         for(var i = 0; i < events.length; i++){
             var day = events[i].day;
             output.append('<div class="event">');
             output.append('<div class="day">' + day + '</div>');
             output.append('<div class="title">' + events[i].title + '</div>');
             output.append('<div class="place">' + events[i].place + '</div>');
             output.append('</div>');
         }
      ?>
  </div>
  <div id="map" class="block">
    <!-- GASのgetMap()で作成したGoogle StaticMapを表示する -->
    <div>
      <img src="<?= getMap(); ?>" alt="map">
    </div>
  </div>
</div>

 

スクレイピング

アルクマスケジュールに対し、

  • UrlFetchApp.fetch()で、アルクマスケジュールよりデータを取得
  • response.getContentText()で、レスポンスをXMLとして取得
  • 正規表現でいらない部分を削除

という形のスクレイピングを行い、必要な情報を取得しています。GASの場合は力技な印象です。

function getEvents() {
  // アルクマのスケジュールにアクセス
  var response = UrlFetchApp.fetch("http://www.arukuma.jp/caravan/index.php");

  // 当月のスケジュール部分を取得
  var reg = /<div class="schedule aug">([\s\S]*?)<\/div>/i;
  var match = reg.exec(response.getContentText());
  var content = match[1];

  // 正規表現による置換で、スケジュール部分を整形してゆく  
  // 正規表現で扱いやすくするために、改行とスペースを削除
  content = content.replace(/\s/g, "")
...

 

Static Mapやジオコーディング部分

昔作った時と変わらなかったので、そのまま流用しています。

 

デモサイト

アルクマップ - GAS版

 
現時点だとこんな感じです*3f:id:thinkAmi:20151224231500p:plain

 

ソースコード

GitHubに上げました。

Googleドライブにスクリプトプロジェクトを作成して、.gs.htmlをコピペすれば動作するかと思います。
thinkAmi/ArukuMapGAS

 

参考

*1:先ほど確認したところ、アドベントカレンダー自体がなくなってて残念

*2:そのアドベントカレンダー以降ノーメンテナンス...

*3:またノーメンテナンスに陥りそうですが...