みんなのケータイ
電子マネー残高を知りたかったからPuppeteerでHeadlessしてIFTTTしてみた
【arrows NX F-01K】
2018年3月30日 07:59
arrows NX F-01Kのおサイフケータイ機能にはいつもお世話になっている。普段から使っている電子マネーはモバイルSuicaとnanacoの2つで、だいたい毎日どちらかを使って買い物したり、電車に乗ったりしている。ただ、オートチャージの設定はしていないので、残高が不安なときは改札を通る前や買い物の前にいちいち各アプリを起動して確認していた。
nanacoはともかく、モバイルSuicaはアプリが起動するまでやけに時間がかかったり、時にはエラーになったりすることもあって、急いでいるときはものすごく煩わしい。もうわざわざ残高確認のためだけにアプリを起動したくない。可能なら、スマートフォンの電源ボタンをワンプッシュするだけで複数の電子マネーの残高を確認できる、くらいの手軽さになるとうれしいんだけど……。
本当ならGoogle Payあたりがそういうのに対応してくれるのが一番ありがたいのだけれど、今のところそういった機能も、他のアプリで実現できそうなものも見あたらない。ならば、作るしかないわけだ。でも新たにアプリを作るのは時間がかかりすぎる。今あるサービスやらツールやらをいろいろ組み合わせてなんとかできないだろうか。
そこで考えたのが、電子マネーと連携するWebサービスで表示した電子マネー残高を、どうにかしてプッシュ通知させるというもの。いろいろな方法がありそうなのだが、一番簡単にできそうだったのが、タイトル通りの「PuppeteerでHeadlessしてIFTTT」するというものだった。
なんだか意味がわからない? 筆者も自分で言っていてよくわからない。が、ざっくり言えば、電子マネーの残高が表示されたWebページのスクリーンショットを取得して、それをDropboxの同期フォルダに突っ込んで、そのアップロードを検知したら、画像とともに通知する、という流れ。図にすると以下みたいな感じ。
もうちょっと丁寧に説明しよう。まず電子マネーの残高を取得するために、会社の経費申請・管理に利用している「MFクラウド経費」を使う。このサービスでは、銀行口座やクレジットカード、電子マネーの利用履歴を自動取得できる。それらの残高を一覧できるページもあるので、このスクリーンショットの取得を自動化するのが手っ取り早そうだ。
スクリーンショットの取得にはPC(macOSまたはLinux)上で動く「Headless Chrome」を使う。これは、最近のGoogle Chromeから搭載されている新しい機能で、WebブラウジングをGUIなしで実行できるもの。コマンドラインから実行して、指定したページを読み込み、スクリーンショットを撮ったり、ページ情報を取得できたりする。
でも、これだけだとMFクラウド経費の口座残高ページを表示する前に認証ページが現れてしまう。なので、ログイン操作をするために、Headless Chromeを裏で操作するための「Puppeteer」というツールを使う。Javascriptのコーディングルールで、Webブラウザー上の文字入力やクリックなどをプログラミングし、それを実行できるようにするものだ。
スクリプトでWebブラウザーを起動して、ログインフォームにIDとパスワードを入力させ、さらにボタンをクリックさせることで、口座残高ページにアクセス。そしたらスクリーンショットを撮って、所定のフォルダに保存する。全て不可視状態で実行されるので、PC上での作業が妨げられることはない。
【Puppeteerで実行するスクリプト】(emoney.js)
const puppeteer = require('puppeteer');
var date = new Date();
var mon = date.getMonth()+1;
var dt = date.getFullYear() + "-" + mon + "-" + date.getDate() + "_" + date.getHours() + "-" + date.getMinutes();
var imgpath = '/※アップロードフォルダ※/'+ dt + '.png';
puppeteer.launch({
headless: true
}).then(async browser => {
const page = await browser.newPage();
await page.setViewport({ width: 1200, height: 800 });
await page.goto('https://expense.moneyforward.com/accounts');
await page.type('#sign_in_session_service_email', '***********'); // ログインIDを指定
await page.type('#sign_in_session_service_password', '***********'); // ログインパスワードを指定
await page.click('[name="commit"]');
await page.waitFor(5000);
await page.screenshot({path: imgpath, fullPage: true});
browser.close();
});
保存先は、ひとまず「Dropbox」で同期している「スクリーンショット」フォルダにした。これによりスクリーンショットを保存した瞬間にDropboxにアップロードされる。あとはこのアップロードを検知して、スマートフォンに通知を送ればいい。
通知を送るのに使ったのは「IFTTT」。指定のトリガーを契機に、指定のアクションを実行する、という自動化が可能になるサービスだ。IFTTTでは、「Dropboxのスクリーンショットフォルダ内に新しいファイルのアップロードを検知したら、それをリッチなプッシュ通知で知らせる」というレシピを作成した。
生成された通知は、スマートフォンの通知パネルやロック画面に表示される。デフォルトでは通知テキストのみしか見えないが、通知を下にスワイプすることでスクリーンショットをプレビューできる。そのスクリーンショットには、見事に電子マネーの残高が表示されている、という仕掛けだ。
もちろん、その時の最新の電子マネー残高を知るためには、この処理を定期的に実行しなければいけない。今回はmacOS上で動作させていたので、macOSの標準機能である「launchd」という定期実行の仕組みを利用して、6時間ごとに残高をチェックするようにした。
【emoneych.jsを定期実行するためのシェルスクリプト】(emoneych.sh)
#!/bin/bash
/usr/local/bin/node /※スクリプトの保存ディレクトリ※/emoneych.js
【emoneych.shを1時間ごとに定期実行するための設定ファイル】(com.ftprt.emoneychecker.plist)
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.ftprt.emoneychecker</string>
<key>ProgramArguments</key>
<array>
<string>sh</string>
<string>/※シェルスクリプトの保存ディレクトリ※/emoneych.sh</string>
</array>
<key>StartInterval</key>
<integer>21600</integer>
<key>RunAtLoad</key>
<true/>
<key>ExitTimeout</key>
<integer>300</integer>
</dict>
</plist>
しかし、今回の方法だと、Macが動作していない限り実行されないし、MFクラウド経費が各口座の情報を再取得する間隔がわりと長いのもネック。IFTTTも、ファイルのアップロードを検知する間隔が数分〜10分前後空いたりするので、リアルタイムに残高確認するような使い方は難しい。ファイル名を新しくしないと検知されず、スクリーンショットを撮るたびにファイルが増えていくのも課題だ。
なので、常時稼働のLinuxサーバー上で動かすとか、ページをスクレイピングしてテキストに整形しアップロードファイルの容量を減らすとか、前回と内容が変わらなかったらアップロードしないとか、いろいろ改善したいところではある。とはいえ、とりあえず、スマホの電源オン&スワイプだけで電子マネー残高を素早く確認できるようになったので、よしとしておきたい。