Rubyで気象庁過去データからのスクレイピング

 # 今回の目的
職場の農業法人で25ha全43枚の畑の管理を任されていまして、枚数と種類が多すぎて作業の管理が不可能でした。
そこで、定植日からの積算温度（１日の平均気温を積算した値）一瞬で確認できるものがあれば便利だと思い作ってみました。


# なにをするのか

https://www.data.jma.go.jp/obd/stats/etrn/index.php

こちらから情報を頂いてきます。天気予報のAPI（ぽいもの）は配信されているようですが、過去の気象データはこの表かcsv形式でしか配信されておりません。
![スクリーンショット 2022-04-14 20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512596/745abb7c-9c96-538d-d4a2-803d5b87dd28.png)

この平均気温の行を取得してきて合計します。（0度以下は0として扱う）
Mechanizeを使ってスクレイピングします。

# ソースコード

```ruby:
require "Mechanize"
require "Date"

def get_accum(prec_no, block_no, correct)        
    y_date = Date.yesterday　　#昨日
    y_year = y_date.year
    y_month = y_date.month
    y_day = y_date.day

    start_date = Date.new(開始日)
    start_year = start_date.year
    start_month = start_date.month
    start_day = start_date.day
    
    diff_month = y_month - start_month
    if y_year == start_year       #開始月と今月が同じ年でなければ差に＋13、同じ年なら＋1
      term_month = diff_month + 1
    else
      term_month = diff_month + 13
    end
    
    ave_temps =[] #空の配列を作成して取得データを追加
    term_month.times do |m|

      if start_month + m > 12 #年を跨ぐとき
        start_year += 1
        start_month -= 12
      end

      url = "http://www.data.jma.go.jp/obd/stats/etrn/view/daily_s1.php?prec_no=#{prec_no}&block_no=#{block_no}&year=#{start_year}&month=#{start_month + m}&day=1&view=" #daily_s1をa1に変えれば各地方局にも対応可能
      agent = Mechanize.new
      page = agent.get(url)
      
      td_length = page.search("//*[@id='tablefix1']/tr").length-4 #td要素の上４つは項目なので、全td要素から4を引いた数がデータの数になる
      
      if m == 0 #スタート月のデータ取得
        (td_length-(start_day-1)).times do |i|
          ave_temp = page.search("//*[@id='tablefix1']/tr[#{5+(start_day-1)+i}]").search('td')[6].inner_text #７番目のtdタグが日平均気温
          ave_temps << ave_temp.delete("^0-9.-") #配列としてave_tempsに順に追加。たまに（）がついてたりするので除去
        end
      elsif m == term_month - 1 #今月のデータ取得
        y_day.times do |i|
          ave_temp = page.search("//*[@id='tablefix1']/tr[#{5+i}]").search('td')[6].inner_text 
          ave_temps << ave_temp.delete("^0-9.-") 
        end
      else     #中間の月は全部取得
        td_length.times do |i|
          ave_temp = page.search("//*[@id='tablefix1']/tr[#{5+i}]").search('td')[6].inner_text 
          ave_temps << ave_temp.delete("^0-9.-") 
        end
      end
    end

    total_temp = 0
    ave_temps.each do |temp|
      if temp.present?              #配列に値があるときのみ計算
        temp = temp.to_f + correct  #ここで補正値計算 
        if temp <= 0                #0度以下はデータとして加算しない。
          temp = 0
        end
        total_temp += temp
      end
    end
    return total_temp.round(2)
  end
```
長ったらしくて汚いですが。。。

```ruby:
term_month.times do |m|
...
end
```
で期間の月の数繰り返し
```ruby:
"http://www.data.jma.go.jp/obd/stats/etrn/view/daily_s1.php?prec_no=#{prec_no}&block_no=#{block_no}&year=#{start_year}&month=#{start_month + m}&day=1&view="
```
prec_noは県の番号。block_noは観測所の番号
現状daily_s1となっているblock_no=47xxxの観測点のみ対応で、これは7列目に平均気温が存在している。
daily_a1の観測点は項目が少ないので4列目にある。このあたり条件分けできればもっと細かい観測地に対応できます。
```ruby:
if m == 0 
#開始月の処理：要素の数（その月の日数）ら開始日を引いたところから取得を開始
elsif m == term_month - 1
#最終月（この場合今月）：初日から昨日の日付まで取得
else
#中間月：全部取得
end
```
てな感じで必要な分だけ取ってきます。

```ruby:
ave_temp = page.search("//*[@id='tablefix1']/tr[#{5+i}]").search('td')[6].inner_text 
```
複雑ですが、id='tablefix1'の中に、tr要素が縦に積まれていて、td要素が横並びに入っている構造です。
なのでtrを５行目から順番に取得して、その中の7番目のtdを持ってこいという意味になります。
![スクリーンショット 2022-04-14 21.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512596/76d979b4-1234-b541-bcf3-f5e9c08c4ccb.png)


あとは配列ave_tempsに全部突っ込んで合計して完了！！

correct(補正値)は基準より寒いor暑いで設定できます。自分の畑は標高差あるので上と下で2〜3℃くらい差があります。

### 注意事項
平均気温はどう頑張っても１日に１回しか更新されないので処理回数に制限をかける等して、必要な時だけに留めましょう。