こんにちは、まろCです。
年末に遊びすぎて風邪を引き、他の人より少し多いけど全然うれしくない11連休を過ごしました。
今回は、PhantomJSをラッパーしたnightmare.jsを使って、WebページのキャプチャのDiffを取り、サイト更新の監視をするものを作ってみます。
LIGブログを見張って一定時間毎にトップページのキャプチャ画像を撮り、1つ前のキャプチャ画像と差異があったとき、記事がリリースされ画面が更新されたと判定します。そうすると、Slackにligblog_watchbotが通知してくれるという仕組みです。
準備
主に使うもの
- node.js
- nightmare -> サイトのキャプチャに使います。
- gm -> 画像のDiffを見るのに使います。
- MongoDB -> キャプチャのログを記録します。
※ソースは全てCoffeeScriptで書いています。
インストール
環境は、Mac OS Xです。
$ brew install mongodb $ brew install imagemagick $ brew install graphicsmagick $ cd ~ $ mkdir kansi $ npm init $ npm install -g phantomjs $ npm install nightmare --save-dev $ npm install mongoose --save-dev $ npm install gm --save-dev $ npm install dateformat --save-dev $ npm install time --save-dev $ npm install cron --save-dev $ npm install request --save-dev $ npm install slack-notify --save-dev</pre>
実装する
1. nightmareでスクリーンショットを保存して、DBにも画像名を保存
capture.coffeeではnightmareを実行し、captureModelでMongooseを使ってMongoDBへの保存を行います。Mongooseでモデルを設定する際、コレクション名を設定しなければ、DB名の複数形になるようです。
caputure.coffee(抜粋)
SETTING = require './setting.coffee' Nightmare = require('nightmare') dateformat = require('dateformat') CaptureModel = require('./captureModel.coffee') class Capture constructor: (@target) ->; @nightmare = new Nightmare() capture: (callback) ->; date = dateformat new Date(), 'yyyymmddHHMMss' imgName = "#{date}.png" @nightmare .goto @target .screenshot SETTING.CAPTURE_PATH+ imgName .run (err, nightmare) ->; #save saveData = new CaptureModel( name: "#{date}.png" createAt: date ) saveData.save (err, newData) ->; console.log err if err return callback(imgName) # captureModel.capture.find (err, data) ->; # console.log data console.log '====Capture capture====', 'capture saved.' module.exports = Capture
caputureModel.coffee(抜粋)
mongoose = require('mongoose') mongoose.connect 'mongodb://localhost/capture', (err) ->; console.log err db = mongoose.connection CaptureSchema = new mongoose.Schema({ name: String createAt: String }) CaptureModel = mongoose.model 'capture', CaptureSchema module.exports = CaptureModel
実行するとキャプチャが保存されます。
背景や、擬似要素のところできちんと表示できない部分がありますね。
2. gmで画像のDiffを調査
compareというメソッドで画像のDiffを調べることができます。2つの画像のパスを渡してあげると結果が返ってきます。
diff.coffee(抜粋)
class Diff constructor: () ->; date = dateformat new Date(), 'yyyymmddHHMMss' @options = file: SETTING.CAPTURE_PATH+ "diff-#{date}.png" highlightColor: 'yellow' tolerance: 0.02 compare: (oldCpature, newCapture, callback) ->; console.log oldCpature, newCapture gm.compare oldCpature, newCapture, @options, (err, isEqual, equality, raw) ->; console.log 'The images were equal: %s', isEqual console.log 'Actual equality: %d', equality console.log raw callback isEqual module.exports = Diff
昨日のスクショと、本日のスクショを比べて、diff.pngとして出力。コンソールにDiffの情報を出力します。
実行すると、optionで設定したパスに画像が出力されています。
画像の差がある箇所が黄色でハイライトされています。
記事が更新されているので、一覧の箇所が黄色になっていますね。The images were equal: false Actual equality: 0.0268236487 Image Difference (MeanSquaredError): Normalized Absolute ============ ========== Red: 0.0311492206 7.9 Green: 0.0285191391 7.3 Blue: 0.0293348867 7.5 Opacity: 0.0182913485 4.7 Total: 0.0268236487 6.8
The images were equal: false ここがfalseのときにSlack通知が行くようにします。
3. 一定間隔で呼んでDBに保存
node-cronを使って定期的に上記の処理を呼びます。
1時間くらいで設定します。
サーバーのcrontabと同じような時間設定ができるんですが、秒から始まるので注意が必要です。app.coffee(抜粋)
SETTING = require './setting.coffee' CronJob = require('cron').CronJob Capture = require './capture.coffee' job = new CronJob( cronTime: '* * */1 * * *' onTick: () ->; capture = new Capture SETTING.TARGET_URL capture.capture (_id) ->; #diff start: true timeZone: "Asia/Toyko" ) job.start()
4. Slackに通知
Slackの画面からIncoming WebHooksの設定をします。
/services/newsからIncomming WebHookをAddします。
Save Settingをクリックしたら準備完了です。node側からPOSTしてあげます。
slack-notifyを使えば一発でした。app.coffee(抜粋)
slack = require('slack-notify')(SETTING.WEBHOOK_URL) slack.send( channel: '#hubot' icon_url: 'https://s3-us-west-2.amazonaws.com/slack-files2/avatars/2014-12-28/3299377210_188baaf32b54f98b381d_36.jpg' text: 'ブログが更新されました。 https://liginc.co.jp', unfurl_links: 1, username: 'ligblog_watchbot' )
こんなかんじでSlackに流れます。
組み合わせて実行
$ coffee app.coffee
最終的な実行コマンドは上のようになります。
こんな感じで動きます。
まとめ
ソースをGitHubに公開しました。
https://github.com/ktkt-/web_watch「イノベーションは組み合わせ」と、誰かが言ってたような気がします。
それは、「モノxモノ」「モノxヒト」「ヒトxヒト」のすべてがそうではないでしょうか。
こういうLIGブログの楽しみ方もありかなと思います。それでは。
【JSでもっとたくさんのことができる!】
※ Node.js完全初心者が、モジュールを作成して、ディレクトリを非同期で読み取り、ファイルをコンソールに出力する方法
※ milkcocoaとgmaps.jsで、スマホ(と、まろ氏)の位置情報をリアルタイムに取得してみた
※ Web制作者でもネイティブアプリが作れる!node-webkitを使ってみよう。
※ チャットツールをGitとNode.jsとHerokuでDIYする方法〜相手作成編〜
※ チャットツールをGitとNode.jsとHerokuでDIYする方法〜画面作成編〜
LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。