花粉の飛散量を取得する Ruby スクリプト

動機

ちょっと前に見かけていた記事ですが、自分も花粉症に苦しんでいるということもあり、Nadoka さんIRC Bot に仕込もうと思って Ruby で書いてみました。 :-p

環境省が運用する花粉観測システム(はなこさん)では1時間毎の花粉飛散データが公開されています。 1時間毎に花粉飛散データが更新されるため外出のタイミングを決定するのに役立ちます。 プログラマであれば、このデータを cron で定期的に監視して警告メールを送ったり、Shell のプロンプトやemacs のミニバッファに表示させたい、と思うはずです。

そこで、この花粉観測システムはなこさんから花粉飛散データを取得する perl module を作成しました。


花粉情報にしか興味がなかったので、風向や風速情報はとっていません。ほんとは scraping したほうがよかったのかもしれませんが、そこも手を抜いちゃいました。

自分としては、Net::HTTP での Cookie の使い方と、Ruby 1.9 での Array#to_s の動きが勉強になったかな、と。
# Nadoka さんを Ruby 1.9 で動かしてるわけじゃないんですが、そのままポーティングするのも芸がなかったので、なんとなく 1.9 対応...


動作確認

東京の観測所は以下となります。今の季節はまだ、都内はさほどではないですが、多摩森林科学園の花粉量はハンパないです。

エリアコード 観測所コード 観測所
3 51310200 東京都多摩小平保健所
3 51320100 独立行政法人森林総研多摩森林科学園
3 51300100 日本医科大学付属多摩永山病院
3 51300200 日本医科大学付属病院

12時時点で動かしてみたら、以下の結果でした。飛散量が 1000 を越えると相当危険らしいです。
# 自分は 100個未満の都市部でも、かなりむずがゆいですが...。

% ruby191 hanako.rb 51310200 3
最新の花粉飛散量は、32 [個/m3]
9 時の花粉飛散量は、24 [個/m3]

% ruby191 hanako.rb 51320100 3
最新の花粉飛散量は、520 [個/m3]
9 時の花粉飛散量は、61 [個/m3]

% ruby191 hanako.rb 51300100 3
最新の花粉飛散量は、86 [個/m3]
9 時の花粉飛散量は、20 [個/m3]

% ruby191 hanako.rb 51300200 3
最新の花粉飛散量は、69 [個/m3]
9 時の花粉飛散量は、8 [個/m3]

Array#to_s の挙動

Ruby 1.8 では Array#join、Ruby 1.9 では Array#inspect と同じ動きになります。

% irb186
>> %w(a b).to_s
=> "ab"
>> %w(a b).inspect
=> "[\"a\", \"b\"]"
>> %w(a b).join
=> "ab"
>> exit

% irb191
>> %w(a b).to_s
=> "[\"a\", \"b\"]"
>> %w(a b).inspect
=> "[\"a\", \"b\"]"
>> %w(a b).join
=> "ab"
>> exit

スクリプト

Ruby 1.9 じゃないと動きません。
プロキシの設定ができるようにしてますが、経由の必要がなければ適宜書きかえてください。

# coding: utf-8
require 'net/http'

class Hanako

  Net::HTTP.version_1_2
  PROXY_HOST = "xxx"
  PROXY_PORT = yyy
  HANAKO_BASE_HOST = "kafun.taiki.go.jp"

  attr_reader :kafun, :mst_code, :area_code

  def initialize(mst_code = "51300200", area_code = "3")
    @kafun = { }
    @mst_code   = mst_code
    @area_code  = area_code
    request_and_parse(mst_code, area_code)
  end

  def now
    now = @kafun.keys.sort.last
    @kafun[now]
  end

  private
  def request_and_parse(mst_code, area_code)
    cookie = nil
    Net::HTTP::Proxy(PROXY_HOST, PROXY_PORT).start(HANAKO_BASE_HOST) do |http|
      response, = http.get("/Hyou0.aspx?MstCode=#{mst_code}&AreaCode=#{area_code.rjust(2,"0")}")
      response.error! unless response.is_a? Net::HTTPOK
      cookie = response.get_fields("Set-Cookie").join.split(";").first
    end

    Net::HTTP::Proxy(PROXY_HOST, PROXY_PORT).start(HANAKO_BASE_HOST) do |http|
      response, = http.get("/Hyou2.aspx", "Cookie" => cookie)
      response.error! unless response.is_a? Net::HTTPOK
      parse(response)
    end
  end

  def parse(response)
    response.body.each_line do |line|
      if /<td><font size=\"2\">([0-9]*)時<\/font><\/td><td align=\"Right\"><font size=\"2\">([0-9]*)<\/font><\/td>/ =~ line.force_encoding("UTF-8")
        @kafun[$1.to_i] = $2.to_i
      end
    end
  end
end

# == how to use ==
hanako = Hanako.new ARGV[0], ARGV[1]
puts "最新の花粉飛散量は、#{hanako.now} [個/m3]"
puts "9 時の花粉飛散量は、#{hanako.kafun[9]} [個/m3]"