ElectronでGoogleカレンダーの日本の祝日を取得する

とあるアプリで、祝日判定をする為に祝日データが必要になったのですが、Googleには公開カレンダーとして日本の祝日というものが用意されています。このデータを取得してデータベースに格納しておけば、手動で祝日登録を行う必要がなくなるので便利です。

ということで、このデータをElectron(Node.js)で取得し、SQLiteのデータベースに格納してみようと思います。

また、今回はいい加減Deprecatedになったrequestモジュールを卒業し、node-fetchモジュールで社内Proxy超えを設定して取得を試みます・

事前準備

API Keyを取得する

Googleカレンダーにアクセスする為には、Cloud ConsoleよりAPI Keyを生成して取得する必要があります。以下の手順で取得をしておきましょう。予めプロジェクトは作っておいてください。

  1. Google Cloud Consoleにログインする
  2. サイドバーを開き、「APIとサービス」を開く
  3. 上部にある「APIとサービスの有効化」をクリックする
  4. calendarで検索し、Google Calendar APIをクリックし、有効化をクリックする
  5. そのまま認証情報を作成するのではなく、一旦APIとサービスに戻り、認証情報をクリックする
  6. 上部にある「認証情報を作成」をクリックする
  7. APIキーをクリックする
  8. すぐにAPIキーが生成されるので、控えておく。
  9. キーを制限をクリックする
  10. 今回はAPIの制限にて、キーを制限をクリックし、Google Calendar APIでだけ利用できるようにします。
  11. 最後に保存するで完了です。

図:APIキーを生成する画面

モジュールを追加する

旧来のrequestモジュールに変わって、node-fetchモジュールを追加します。しかし、node-fetch自身はProxyに対応していないので、追加でさらにproxy-agentモジュールも追加します。ターミナルから以下のモジュールをElectronのプロジェクトに追加しましょう。

npm i node-fetch
npm i proxy-agent

もちろん、Proxyの無い環境であれば不要なので、ここではnode-fetchだけをインストールすればOKです。

ソースコードとレスポンス

index.js側コード

//モジュールを追加する
const fetch = require('node-fetch');
var ProxyAgent = require('proxy-agent');

//Google Calendar関係
var apikey = "ここにAPIキーを入力する";
var calid = "ja.japanese#holiday@group.v.calendar.google.com";
var apiurl = "https://www.googleapis.com/calendar/v3/calendars/";

//Proxy URLを指定する
var agent = new ProxyAgent("http://hogehoge.com:8080");

//Googleカレンダーの日本の祝日データを取得する
ipcMain.on('getholiday', function( event ){
  //日付の指定
  var startday = "2021-01-01";
  var endday = "2021-12-31";

  //リクエストURLを構築する
  var requrl = apiurl + encodeURIComponent(calid) + "/events?key=" + apikey + "&timeMin=" + startday + "T00:00:00.000Z" + "&timeMax=" + endday + "T23:59:59.000Z";

  var options = {
    method: 'GET',
    agent: agent,
    headers: { 'Content-Type': 'application/json' },
  }

  //ステータスコード用
  var status

  fetch(requrl, options)
  .then((res) => { 
    //ステータスコードを取得
    status = res.status; 

    //body部分を取得
    return res.json() 
  })
  .then((jsonData) => {
    if(status == 200){
      //レコードを取得
      var record = jsonData.items;
     
      //取得した祝日情報をSQLiteに追加する
      var query = "DELETE FROM holiday_public";
      sqlitedelete(query,function (ret){
        if(ret == "NG"){
          event.sender.send("closeman");
          return done();
        }
    
        async.eachSeries(record,function(rec,next){
          //西暦、月の取得
          var tempdate = new Date(rec.start.date);
          var seireki = tempdate.getFullYear();
          var monthman = tempdate.getMonth() + 1;
          var fulldate = getdateman(tempdate);

          //クエリを構築
          query = "insert into holiday_public (seireki,month,hdate,hname) values (?,?,?,?);"
          var values = [seireki,monthman,fulldate,rec.summary];

          sqliteinsert(query,values,"holiday_public",function (ret){
            if(ret[0] == "NG"){
                event.sender.send("error");
                return done();
            }
        
            //次の処理へ
            next();
            return;
          });
        },function(err){
          //エラートラップ
          if(err){
            //メッセージを返して処理を中断
            event.sender.send('message', err);
            return;
          }

          //完了通知
          event.sender.send('holigetend','祝日の取得が完了しました。');
        });
      })
    }else{
      event.sender.send('message', status + "エラー:\n" + jsonData.error.message);
    }
  })
  .catch((err) => {
    //エラーメッセージ
    event.sender.send('message', status + "エラー:\n" + err);
    return;
  });
});
  • Google CalendarのIDはja.japanese#holiday@group.v.calendar.google.comで、リクエストURLに指定する場合はencodeURIComponentでURLエンコードする必要があります。
  • startdayとenddayで日付の範囲を指定しますが、Google Calendarの場合、2年後以降の祝日データは入っていないので注意。
  • node-fetchでproxyを透過するには、optionsの中のagentにて指定する必要があります。投稿を表示
  • レスポンスはres.json()で取得され、これはBody部分のデータが入っています。
  • ステータスはres.statusで取得し判定することが可能です。
  • sqlitedeleteとsqliteinsertは自前で用意したSQLiteのデータ削除とデータ追加の為の関数です。
  • async.eachSeriesにて1レコードずつ追加しています。
  • node-fetch以外にaxiosというリクエストモジュールがあるのですが、自分の環境ではどう頑張ってもproxy関係で躓いてNGだったのでスルーしました。(2021/08/04 - なんかProject Deadという不穏なスレッドが立っています。利用者多いはずなのですが大丈夫かな?)

レスポンスデータ

{
    "kind": "calendar#events",
    "etag": "\"p32br1nf4k31v20g\"",
    "summary": "日本の祝日",
    "updated": "2021-06-30T16:26:03.000Z",
    "timeZone": "UTC",
    "accessRole": "reader",
    "defaultReminders": [],
    "nextSyncToken": "CMD54u3iv_ECEAAYASC2qIy2AQ==",
    "items": [
        {
            "kind": "calendar#event",
            "etag": "\"3235834125780000\"",
            "id": "20210101_huquf92av88tta92reljcibi1s",
            "status": "confirmed",
            "htmlLink": "https://www.google.com/calendar/event?eid=MjAyMTAxMDFfaHVxdWY5MmF2ODh0dGE5MnJlbGpjaWJpMXMgamEuamFwYW5lc2UjaG9saWRheUB2",
            "created": "2021-04-08T21:24:22.000Z",
            "updated": "2021-04-08T21:24:22.890Z",
            "summary": "元日",
            "creator": {
                "email": "ja.japanese#holiday@group.v.calendar.google.com",
                "displayName": "日本の祝日",
                "self": true
            },
            "organizer": {
                "email": "ja.japanese#holiday@group.v.calendar.google.com",
                "displayName": "日本の祝日",
                "self": true
            },
            "start": {
                "date": "2021-01-01"
            },
            "end": {
                "date": "2021-01-02"
            },
            "transparency": "transparent",
            "visibility": "public",
            "iCalUID": "20210101_huquf92av88tta92reljcibi1s@google.com",
            "sequence": 0,
            "eventType": "default"
        }
    ]
}
  • itemsの中に複数の祝日データが入っています。
  • res.json()で取得する中身になります
  • 通常は、itemsのsummarystartのdateの値だけで十分です。それぞれ祝日名と日付になります。

図:無事に取得出来ました。

関連リンク

コメントを残す

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

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