Ruby + Mechanize で Yahoo! JAPAN の花粉飛散予報を取得する(2018年版)

## 概要

Web サイト [花粉対策特集 \- Yahoo\!特別企画](https://kafun.yahoo.co.jp/) を Ruby + Mechanize でスクレイピングして花粉飛散予報を取得する。

## 花粉飛散情報について

- 花粉飛散予報は今日と明日の2日分
- 沖縄県の花粉情報は無い
- 飛散量には「少ない」「やや多い」「多い」「非常に多い」「飛散前」「飛散終了」「情報なし」などの文字列が入ると思われる

参考: [名古屋市中区の花粉情報 \| 花粉症特集 \- Yahoo\!特別企画](https://kafun.yahoo.co.jp/weather/23/23106/)

> ![kafunrank.png](https://qiita-image-store.s3.amazonaws.com/0/139365/cb428024-d329-a5c3-e8f3-67a1e4e34b02.png)

## Ruby によるサンプルコード

スクレイピングのためのライブラリとして [mechanize](https://rubygems.org/gems/mechanize/) を利用する。

```ruby
require 'mechanize'

# 花粉飛散情報。
#
# データソース:
# 花粉対策特集 - Yahoo!特別企画 {https://kafun.yahoo.co.jp/}
class Kafun

  # 花粉飛散情報を取得する。
  #
  # @param pref_id [String]     JIS X 0401 都道府県コード
  # @param detail  [true/false] 詳細情報(2日分の花粉予報)を取得するか？
  # @return [Hash]
  def get(pref_id, detail=false)

    # URLの例
    # 北海道: https://kafun.yahoo.co.jp/weather/1/ ← 「01」じゃなくて「1」
    # 愛知県: https://kafun.yahoo.co.jp/weather/23/
    # 沖縄県のデータは無し
    # 都道府県コードが「01」で来ても「1」になるように to_i
    url = "https://kafun.yahoo.co.jp/weather/#{pref_id.to_i}/"

    agent = Mechanize.new
    page  = agent.get(url)

    date = page.search('.date').inner_text
    area = page.search('.dataBox__areaName a')
    data = page.search('.dataBox__pollenData .text')
    kind = page.search('.dataBox__pollenKind .text')

    area_list = []
    area.each_with_index do |e, i|
      area_list << { # 地域毎にデータを持つ構造に
        :area => e.inner_text, # 地域名
        :url  => "https:#{e[:href]}", # URL
        :data => [{ # 花粉飛散情報(日付別データの配列)
          :date => date, # 日付
          :rank => data[i].inner_text, # 飛散量
          :kind => kind[i].inner_text # 種類
        }]
      }
    end

    if detail
      detail_area_list = []
      area_list.each do |e|
        # 2日分の花粉飛散情報を取得
        detail_area_list << get_detail(e[:url], e[:area])
      end
      detail_area_list
    else
      # 1日分の花粉飛散情報を返す
      area_list
    end
  end

  private

  def get_detail(url, area)

    agent = Mechanize.new
    page  = agent.get(url)

    date = page.search('.dataBox__date')
    data = page.search('.dataBox__pollenData .text')
    kind = page.search('.dataBox__pollenData .text__kind')

    list = [] # 花粉飛散情報(日付別データの配列)
    date.each_with_index do |e, i|
      list << {
        :date => e.inner_text, # 日付
        :rank => data[i].inner_text, # 飛散量
        :kind => kind[i].inner_text # 種類
      }
    end

    {
      :area => area, # 地域名
      :url  => url, # URL
      :data => list # 花粉飛散情報(日付別データの配列)
    }
  end
end

puts '愛知県(都道府県コードは23)の花粉飛散情報を取得する(1日分)'
pp Kafun.new.get('23')
puts

puts '愛知県(都道府県コードは23)の花粉飛散情報を取得する(2日分)'
pp Kafun.new.get('23', true)
puts
```

## サンプルコードの実行結果

```
愛知県(都道府県コードは23)の花粉飛散情報を取得する(1日分)
[{:area=>"名古屋市中区",
  :url=>"https://kafun.yahoo.co.jp/weather/23/23106/",
  :data=>[{:date=>"1月31日", :rank=>"飛散前", :kind=>"スギ花粉中心"}]},
 {:area=>"豊橋市",
  :url=>"https://kafun.yahoo.co.jp/weather/23/23201/",
  :data=>[{:date=>"1月31日", :rank=>"飛散前", :kind=>"スギ花粉中心"}]},
 {:area=>"岡崎市",
  :url=>"https://kafun.yahoo.co.jp/weather/23/23202/",
  :data=>[{:date=>"1月31日", :rank=>"飛散前", :kind=>"スギ花粉中心"}]},
 {:area=>"設楽町",
  :url=>"https://kafun.yahoo.co.jp/weather/23/23561/",
  :data=>[{:date=>"1月31日", :rank=>"飛散前", :kind=>"スギ花粉中心"}]}]

愛知県(都道府県コードは23)の花粉飛散情報を取得する(2日分)
[{:area=>"名古屋市中区",
  :url=>"https://kafun.yahoo.co.jp/weather/23/23106/",
  :data=>
   [{:date=>"1/31（水）", :rank=>"飛散前", :kind=>"スギ花粉中心"},
    {:date=>"2/1（木）", :rank=>"少ない", :kind=>"スギ花粉中心"}]},
 {:area=>"豊橋市",
  :url=>"https://kafun.yahoo.co.jp/weather/23/23201/",
  :data=>
   [{:date=>"1/31（水）", :rank=>"飛散前", :kind=>"スギ花粉中心"},
    {:date=>"2/1（木）", :rank=>"少ない", :kind=>"スギ花粉中心"}]},
 {:area=>"岡崎市",
  :url=>"https://kafun.yahoo.co.jp/weather/23/23202/",
  :data=>
   [{:date=>"1/31（水）", :rank=>"飛散前", :kind=>"スギ花粉中心"},
    {:date=>"2/1（木）", :rank=>"少ない", :kind=>"スギ花粉中心"}]},
 {:area=>"設楽町",
  :url=>"https://kafun.yahoo.co.jp/weather/23/23561/",
  :data=>
   [{:date=>"1/31（水）", :rank=>"飛散前", :kind=>"スギ花粉中心"},
    {:date=>"2/1（木）", :rank=>"少ない", :kind=>"スギ花粉中心"}]}]
```

## 参考資料

- [mechanize \| RubyGems\.org \| your community gem host](https://rubygems.org/gems/mechanize)
- [花粉対策特集 \- Yahoo\!特別企画](https://kafun.yahoo.co.jp/)
- [全国地方公共団体コード \- Wikipedia](https://ja.wikipedia.org/wiki/%E5%85%A8%E5%9B%BD%E5%9C%B0%E6%96%B9%E5%85%AC%E5%85%B1%E5%9B%A3%E4%BD%93%E3%82%B3%E3%83%BC%E3%83%89)