1
Help us understand the problem. What are the problem?

posted at

updated at

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

今回の目的

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

なにをするのか

こちらから情報を頂いてきます。天気予報のAPI(ぽいもの)は配信されているようですが、過去の気象データはこの表かcsv形式でしか配信されておりません。
スクリーンショット 2022-04-14 20.png

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

ソースコード

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要素の上4つは項目なので、全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 #7番目の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

長ったらしくて汚いですが。。。

term_month.times do |m|
...
end

で期間の月の数繰り返し

"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列目にある。このあたり条件分けできればもっと細かい観測地に対応できます。

if m == 0 
#開始月の処理:要素の数(その月の日数)ら開始日を引いたところから取得を開始
elsif m == term_month - 1
#最終月(この場合今月):初日から昨日の日付まで取得
else
#中間月:全部取得
end

てな感じで必要な分だけ取ってきます。

ave_temp = page.search("//*[@id='tablefix1']/tr[#{5+i}]").search('td')[6].inner_text 

複雑ですが、id='tablefix1'の中に、tr要素が縦に積まれていて、td要素が横並びに入っている構造です。
なのでtrを5行目から順番に取得して、その中の7番目のtdを持ってこいという意味になります。
スクリーンショット 2022-04-14 21.41.png

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

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

注意事項

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

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
1
Help us understand the problem. What are the problem?