西友 5%OFF 開催日カレンダーをスクレイピングして Google カレンダーとして公開しました

元ネタ:スーパーの5%OFF開催日をiCal形式で配信してGoogleカレンダーに表示してみた

はじめに

私は毎日に西友に行くほどの西友ヘビーユーザーなので、元ネタに触発されて、西友 5%OFF 開催日の Google カレンダーを作って公開しました。

自分の Google カレンダーに追加するには、上記カレンダーの URL を開いて右下のボタンを押すか、Calendar ID を [友だちのカレンダーを追加] に入力してエンターを押すとできます。

元ネタでは AWS Lambda 上でスクレイピングを実行した結果を iCal 形式で配信し、それを Google カレンダーに追加するという形をとっています。

AWS Lambda は無料枠があるため個人で使う程度であれば無料ですが、カレンダーを一般公開しようとすると課金される可能性があります。 そこで、完全無料で定期的に動作させることを目指し、結果としては Google カレンダー、Google Apps Script、Travis CI cron job を使うことで完全無料を実現しました。

スクレイピングのプログラムは GitHub で公開しています。
https://github.com/kojole/seiyu-5off

実装について

スクレイピング

HTTP クライアントは node-fetchスクレイピングには jsdom を使いました。 スクレイピングのライブラリに特に詳しくはなかったので、ブラウザ環境で使うことに慣れている fetchDOM と同様に扱えることを理由に選びました。

スクレイピングした開催日のデータは、リポジトリ中に JSON ファイルとして保存しています。 Travis CI の cron Job を使って定期的にスクレイピングを行い、自動的に JSON ファイルを更新するようにしました。 Travis CI からリポジトリにコミットする方法については、Travis CIのcron jobsを使ってGitHubに定期的にcommitする方法 が非常に参考になりました。

Google カレンダーのイベント作成

Google API クライアントである google-api-nodejs-client を使おうとすると OAuth 周りが少し面倒なので、Google Apps Script を使ってカレンダーにイベントを作成するようにしました。

リポジトリから開催日データの JSON を取得してカレンダーにイベントを作成するだけの割と単純な機能だったので、今回はモジュールバンドラを使用せず生の JavaScript (ES5) で書きました。

モジュールを使わないので単体テストを実行するのに少し工夫が必要です。例えばテスト対象のファイルを index.js とすると、テストファイルの先頭でテスト対象のファイルを読み込んで、vm.runInContext で対象ファイルを実行する必要があります。なお、テストには jest を使っています。

// index.test.js
const fs = require('fs');
const path = require('path');
const vm = require('vm');

const code = fs.readFileSync(path.join(__dirname, 'index.js'));
const globalContext = vm.createContext(global);
vm.runInContext(code, globalContext, { filename: 'index.js' });

describe('', () => {
  // ...
});

まとめ

西友 5%OFF 開催日をスクレイピングで取得し、Google カレンダーとして公開しました(リンク)。 Travis CI cron job と Google Apps Script を使って定期的に開催日データが更新される仕組みを作りました。

全然関係ないですが、西友のセルフレジの重さ判定で時々エラーになって店員さんに対応してもらうとき、少し居心地が悪く感じてしまいます。