ウェブサイトの更新を監視するプログラムを作ってみた

Rubyでウェブサイトを取得できる[Watir](https://watir.com/)というgemを使って、ウェブサイトの簡易更新チェッカーを作ってみました。

#必要なもの
- Rubyインタプリタ
    - 今回はRuby 2.3.1で試しました。
- Watir
    - `gem install watir`
- PhantomJS
    - http://phantomjs.org/ からダウンロード

#コード
```rb:update_checker.rb
#!/usr/bin/env ruby

require 'watir'

class UpdateChecker
  def initialize(url, interval = 180)
    @browser = Watir::Browser.start(url, :phantomjs)
    @interval = interval
    @old = nil
  end

  def start(&block)
    Thread.new do
      loop do
        s_time = Time.now
        check(&block)
        sleep [0, @interval - (Time.now - s_time)].max
      end
    end
  end

  def check
    new = nil
    @browser.refresh
    if block_given?
      new = yield(@browser)
    else
      new = @browser.html
    end

    unless @old.nil?
      if @old == new
        not_updated(@old)
      else
        updated(@old, new)
      end
    end
    @old = new
  end

  def updated(old, new)
    puts "#{@browser.title} updated on #{Time.now}"
    puts new
  end

  def not_updated(old)
    puts "#{@browser.title} not updated"
  end
end
```

#使い方
##例
[Yahoo! Japan](http://www.yahoo.co.jp/)のトップページのニュースの更新をチェックする例です。

```rb
checker = UpdateChecker.new("http://www.yahoo.co.jp/")

def checker.updated(old, new)
  puts "latest news!"
  puts new
end

thread = checker.start do |browser|
  browser.div(id: "topicsboxfb").ul(class: "emphasis").text
end
thread.join
```
##解説
<dl>
<dt>UpdateChecker.new</dt>
<dd><code>UpdateChecker.new(url, interval)</code>でオブジェクトを作成します。<code>url</code>は監視するサイトのURL、<code>interval</code>はチェックする間隔（秒）です。<code>interval</code>を省略すると3分ごとにチェックしに行きます。</dd><br />

<dt>def checker.updated(old, new) 〜 end</dt>
<dd>チェックの結果、更新があった時には<code>UpdateChecker#updated(old, new)</code>が、なかった時には<code>UpdateChecker#not_updated(old)</code>が呼び出されます。
なので、継承や特異メソッドなどでこれらのメソッドを再定義すればチェック後の挙動を変更することができます。例えばメールを送るようにしたりなども可能ですね(・∀・)<br />
ちなみに引数のold,newは更新前と更新後のページのソースです。</dd><br />

<dt>checker.start</dt>
<dd><code>checker.start</code>で監視を開始します。戻り値は監視を行うThreadオブジェクトです。</dd><br />

<dt>do |browser| 〜 end</dt>
<dd><code>checker.start</code>はデフォルトではページのソース全体を比較しますが、ブロックを渡すとそのブロックの戻り値を比較して更新されたかを判断します。また、<code>updated</code>や<code>not_updated</code>の引数もこの戻り値が使われます。<br /><br />
ブロック引数<code>browser</code>は<code>Watir::Browser</code>オブジェクトになります。詳しい使い方は述べません（というか僕もよくわかってません）が、たとえば<code>browser.div(id: "hoge")</code>とすると<code>&lt;div id="hoge"&gt;〜&lt;/div&gt;</code>を表す<code>Watir::Element</code>オブジェクトが取得できます。このオブジェクトに対してさらにメソッドをチェーンすると子要素や孫要素が取得できます。<br />
id以外にもclassなどどんな属性でも検索できるようですが、同じタグが複数あった場合は最初に見つかったものが返ります。またXpathやCSSセレクタも使えるようです。<br /><br />
上の例ではidでdiv要素を検索し、その子孫要素（直接の子要素でなくても可）のul要素を取っています。<code>text</code>メソッドは子テキストノード（すべてのテキストノードを連結したもの）を取得します。結果、ブロックの戻り値はこんな感じです。（2016/10/24 15:30頃のヤフーニュース）
<pre>
石原氏にヒアリング再要請へ動画
保育士殺害 被害者の家狙った動画NEW
ハロウィン 法に触れる仮装は写真
小柴さん設立 科学財団解散へ
iPhone7 Plus モデムで速度差NEW
兵役対応 鳥栖主将Kリーグへ写真NEW
平岳大 父死去でコメント写真
肝付さん死去 声優仲間ら追悼写真NEW
</pre>
</dd>

<dt>thread.join</dt>
<dd>そのままだとRubyインタプリタが終了してしまうので、<code>checker.start</code>が返したスレッドを<code>Thread#join</code>します。</dd>
</dl>

#できること
- 通常のページの更新チェック
- Javascriptで動的に生成されるページの更新チェック

#できないこと
- プログラム終了時から次回起動時までの間の更新のチェック

#参考
APIのドキュメント ： http://www.rubydoc.info/gems/watir-webdriver