大学シャトルバスのタイムテーブルを呟く Bot

まぁ今更 TwitterBot を作ったところで特に目新しい話が出る訳ではありませんが、備忘録という事で。先ず、うちの大学のシャトルバス運行タイムテーブルが大学のアクセスとして載せられていまして、バスに乗ろうという時にはわざわざそこにアクセスするか、表を持ち歩かないと駄目でした。まぁ両方めんどくさいんで、よく見てる Twitter に情報を定期 post すれば良いんじゃない? っていう安直な発想です。

スクレイピング

先ず、作り始める上で重要なのはどこから元になるデータを調達するのか? というところです。コレは自分が手作業で打ち込み業務を行ってもなんら問題はなく、その場合はこの章は無駄になる訳ですが、Web の情報且つ現在運営されている法人のサイトですのでいつタイムテーブルが変更されるか分かりません。ですので、タイムテーブルが変わっても自動更新出来る Bot にする必要がありました。何せめんどくさいですから。自動更新するには紙からの入力はあり得ないので、結果 Web から取得する事になりました。

と、言う訳でスクレイピング*1する必要があります。言語は Ruby でライブラリはスクレイピングに nokogiri, tweet 用に twitter を使いました。

Gemfile の作成から。

source :rubyforge
gem 'twitter', '4.5.0'
gem 'nokogiri', '1.5.6'
$ sudo gem install bundler
$ bundle install --path vendor/bundler

これで準備が ok です。

  • nokogiri の使い方
require 'nokogiri'
require 'open-uri'

bus_schedule = "http://#{URI}"
doc = Nokogiri::HTML(open(bus_schedule))

# Xpath or CSS
doc.xpath("xpath")
doc.css("attr")

Xpath, CSS 共に指定した要素とマッチする場合、マッチ箇所を DOM として返されます。後は each なり map なりを使って個別に node を取り出して調理するだけでした。簡単。

参考ページは以下です。

コレで、Web ページからタイムテーブルの取得は出来たので、ほとんど終わり*2です。
次は Tweet する機能の話。

Tweet 機能

とは言え、こちらも正直な所 twitter ライブラリを使えば後は登録してポンなので別段特別な事はしておらず。以下の通りです。

requrie 'tiwtter'
require 'yaml'

config_file = "ENV['HOME']/.key.yml"
env = YAML::load File.open(config_file)

Twitter.configure do |config|
  config.consumer_key = CONSUMER_KEY
  config.consumer_secret = CONSUMER_SECRET
  config.oauth_token = OAUTH_TOKEN
  config.oauth_token_secret = OAUTH_TOKEN_SECRET
end

client.update("テストツイーョ")

やった事と言えば ${HOME}/.key.yml にトークンと鍵を置いて読み込むようにした程度です。次でも説明しますが、.key.yml は諸事情の為ホームディレクトリに置いています。

スクリプトdaemon

Ruby 1.9 からスクリプトdaemon*3 化する為のメソッドが追加されました。Process.daemon を実行すると、スクリプトdaemon 化します。ただし、注意点として、カレントディレクトリが変更される (らしい) ので絶対 PATH で書いています*4

仕上げ

Bot の作成という事で、勝手に死なれては困ります。なので、tweet のタイミングで Error ハンドリングを行って呟きに失敗したら時間を空けてトライするようにしました。

require 'time'

Process.daemon

while true
  begin
    if Time.now.min % 10 == 0 and (7 <= Time.now.hour and Time.now.hour <= 23)
      tweet = "テストツイーョ"
      client.update(tweet)
    end

    sleep 60
  rescue => ex
    sleep 10
    retry
  end
end

上から、プロセスをデーモン化し、無限ループによって tweet する Bot の完成。sleep を入れているのは、無意味にマシンのリソースを食い潰すのを防ぐ意味と、Twitter への DOS 攻撃にならないようアクセス制限を設ける意味があります。begin, rescue, end の構文はエラー処理ですね。エラーが出た時は 10 秒待機した後、処理のやり直しを行うだけです。後は、何回も連続して呟きに行かないよう呟きが成功した場合は 1 分待ち、条件分岐で現在時刻の分が 10 で割り切れる時間の時且つ、7 時 ~ 23 時にのみ tweet 可能にすれば無意味な Twitter へのアクセスはなくなります。


*1:Web 上に公開されている情報の内、必要な部分のみを取得する事

*2:まぁデータを巧い具合に整形して、奇麗に出力出来るようにするには時間かかりましたが

*3:プログラムを常駐させる事

*4:${HOME} に置いたのは記述が簡潔且つ環境に依存しないから