数十行の簡単なスクリプトでWebコンテンツの更新を検知し通知する

以前にも似たようなのは書いたのだけど。
某アイドルグループに、ついに待望のファンクラブが作られた。もちろんすぐに入会しました。
そこではその子たちのマネージャーさんが不定期に日記を更新してくれるのだけど、残念なことにRSSとかも無いし、自分でログインして覗いてみないと更新されているかどうかを知ることができない。


自動化せずにはいられない。


ということで、更新チェックするプログラムを書くのだけど、、、

コンテンツが「更新されたか否か」を判定するためには、「一定間隔でコンテンツの内容をチェック」し、「前回チェックしたときと比較して差異があるか否か」を調べる必要がある。
「一定間隔で内容をチェック」すること自体は、cronなどを使えば簡単にできる(ここではWWW::Mechanizeでログイン、Web::ScraperでコンテンツHTMLの解析を行う)。
けど、「前回チェックしたときと比較して差異があるか否か」を調べるためには、前回チェックしたときの結果を何らかの形で残しておく必要がある。ファイルに書き出すだとか、DBに保存するだとかの形で。
そうするのは面倒なので、そもそもプログラムを終了させる形で書かずに「一定間隔で内容チェック」だけを行うよう実行させ続け、チェックした結果はメモリに残しておくようにする。
AnyEventのtimerで一定間隔ごとにMechanizeを走らせるようにする。

#!/usr/bin/env perl
use strict;
use warnings;
use utf8;

use AnyEvent;
use Config::Pit;
use Encode 'encode_utf8';
use Log::Minimal;
use Try::Tiny;
use Web::Scraper;
use WWW::Mechanize;

my $conf = +{
    angeleyes => pit_get('fc.momoclo.net', require => {
        login_id => '会員番号',
        password => 'パスワード',
    }),
};
my $previous = undef;

my $cv = AE::cv;
my $w = AE::timer 0, 100, sub {
    try {
        my $mech = WWW::Mechanize->new;
        $mech->get('https://fc.momoclo.net/pc/login.php');
        $mech->submit_form(
            form_id => 'loginForm',
            fields  => $conf->{angeleyes},
        );
        my $latest = scraper {
            process '#topdiary .cont li', 'contents[]' => scraper {
                process 'a',      'title' => 'TEXT';
                process 'p.date', 'date'  => 'TEXT';
            };
        }->scrape($mech->content)->{contents}[0];
        $latest->{date} =~ s/更新//;
        my $current = sprintf '(%s) %s', $latest->{date}, encode_utf8 $latest->{title};
        infof('latest: %s', $current);
        if (defined $previous && $previous ne $current) {
            # 何らかの通知を行う
        }
        $previous = $current;
    } catch {
        warnf('error: %s', $_);
    };
};
$cv->recv;

https://gist.github.com/516f6d3f7297d2865a93


プログラムはAnyEvent::timerによって回り続けるので終了せず、定期的にコンテンツをチェックした内容は$previous変数に保持され続け、更新されたときだけ通知処理を行う、ということがこの単一のスクリプトだけで実現できる。cronを使う必要は無い。


こういうのはNode.jsとかでsetIntervalを使っても一緒だと思うけど、Mechanize的なモジュールをよく知らないのでPerlで書いた。
WWW::Mechanize的なのってどういうのがあるんだろう?


ニッポン笑顔百景

ニッポン笑顔百景

Z女戦争(初回限定盤A)(DVD付)

Z女戦争(初回限定盤A)(DVD付)

追記

少なくともPerlの場合はwhile文でループさせつつsleepで、という方法でも良かったはず。Nodeで最初にsetIntervalで書こうとしていてこういう形になってしまったのかも…