天気予報APIを用いて5日間の天気情報を取得しよう

Ruby

こんにちはkazutoです。今回は、OpenWeatherMapというサイトのAPIを取得して、

  • 東京
  • 大阪
  • 沖縄
  • 北海道

の5日間の天気情報を取得していきましょう。

事前準備

まずは、ディレクトリ構成を確認して、Linuxコマンドを用いて開発環境を整えていきましょう。

mkdir 任意なディレクトリ/WEATHER
touch WEATHER/citys.csv
touch WEATHER/execution.rb
touch WEATHER/weather_class.rb

※weather.jsonファイルに関しては、Fileクラスを用いて書き込む事により生成するのでtouchコマンドを用いて、ファイルを作成しておく必要がありません。

ファイルを作成した所で、実際にファイルが作成されたのかを確認するために、現在のカレントディレクトリから開発環境のディレクトリに移動しましょう。

cd  WEATHER
pwd
ls
citys.csv,execution.rb,weather_class.rb

lsコマンドでファイルの一覧が作成したファイルと一致した場合は、正しく開発環境を整えられたので、次のステップに進みましょう。

続いて、各種ファイルの詳細について確認しましょう。

  • citys.csv
    →都市の詳細
  • execution.rb
    →実行ファイル
  • weather_class.rb
    →Wetherクラス実装
  • tenki.json
    →天気情報をJson化したファイル
  • config.rb
    →APIKEYなどの情報を環境変数から取得するファイル

となります。

各ファイルの詳細を確認できた所で、こちらからOpenWeatherMapにアクセスし、サインアップしてください。サインアップできたら、登録したメールアドレスにAPIKEYが送られてくるので、メモなどに一度、APIKEYを記載して置いてください。(後で使います。)

環境変数の設定

先ほど送られた、APIKEYは、漏洩すると外部から攻撃に合ったり、あなたの個人情報が漏洩する可能性があります。なのでセキリュティ対策のため、外部から参照ができない、環境変数にAPIKEYを格納する必要があります。環境変数とはOSが提供するデータ共有機能の一つです。要するにOSが持っている変数で、OSが設定値などを永続的に保存し、利用者や実行されるプログラムから設定・参照できるようにしたものです。

 export NAME = kazuto
 export -p NAME = kazuto

とすれば環境変数の設定ができます。しかし、ターミナルを再起動してしまうと設定した環境変数の値が失われてしまいます。なので、永続的に環境変数を保持できる様、zshを使っている方であれば、.zshrcファイルに環境変数を記述します。bashを使っている方であれば、/.bash_profileファイルに、環境変数を記述します。

ここで問題点として普段使っているGUI用のテキストエディタでは、ターミナル内のファイルをいじることが出来ない点です。なのでCUI専用のテキストエディタ、vimを使って設定していきましょう。vimはmac環境ではデフォルトで使用できると思います。

なおvimを初めて使う方は、vimチュートリアルがおすすめです。

vimtutor

と上記のコマンド叩いてたらvimチュートリアルの画面に飛べると思うので時間があれば、学習してみてください。

vimを使って環境変数を設定しよう

では、実際に環境変数設定していきましょう。

vim ~/.zshrc

.zshrcファイルに移動できたら下記の様に環境変数を設定してください。

export WEATHER_API_KEY='APIKEYを入力'
export WEATHER_URL="URLを入力"

WEATHER_URLに関しては、天気予報の情報を取得するのに必要なベースのURLになります。

 http://api.openweathermap.org/data/2.5/forecast

こちらのURLもAPIKEYと同じ様に設定して置いてください。(忘れずに:wqで保存しましょう。)

環境変数の設定ができたら、

//zshrcファイルを読み込み
source ~/.zshrc
//環境変数が設定できたか確認
export -p WEATHER_API_KEY
export -p  WEATHER_URL

とコマンドを叩いてください。

ここまでの一連の流れで環境変数の設定ができましたので、config.rbファイルで対象の環境変数を取得しましょう。

config.rbファイルで環境変数を取得しよう

トピックの最後で、config.rbファイルで環境変数を取得していきましょう。まずは、Rubyで環境変数を取得する方法を伝授します。Rubyで環境変数を取得するにはENVオブジェクトを用いる必要があります。

ENV['環境変数名']

上記の様にENV[]とする事で環境変数をRubyファイル内で取り扱う事ができます。では、実際にconfig.rbファイル内で、ENVオブジェクトを用いて環境変数を取得してみましょう。

WEATHER_API_KEY = ENV['WEATHER_API_KEY']
WEATHER_URL     = ENV['WEATHER_URL']

上記は、定数に環境変数を格納しています。環境変数のままだと記述量が多いので、定数に一度、入れ直すという形を取りました。

puts WEATHER_API_KEY
puts WEATHER_URL

環境変数の値が正しく格納されているか、確認しておきましょう。もし環境変数が、正しく設定されていなかったり、表示されなかった場合は、再度、設定しましょう。

以上で、環境変数の設定が終わりになります 。外部APIにアクセスする場合などにAPIKEYが必要な時は、環境変数にAPIKEYを設定しましょう。

天気予報APIを用いて5日間の天気情報を取得しよう[実装編]

それでは、実際に天気情報を取得するプログラムを作成していきましょう。なお、wetherクラスというクラスを定義して実装していきます。

まずは、作成するメソッドについて解説します。

  • initializeメソッド
    →citys.csvから都市の詳細を取得してインスタンス変数に格納
  • select_cityメソッド
    →都市名の表示、選択
  • get_weather_forecastメソッド
    OpenWeatherMapにリクエスト、パース
  • display_weather_forecastメソッド
    →天気情報の表示

となります。

CSVファイルの設定

まず初めに、CSVファイルに都市の詳細についてまとめておきましょう。CSVとは、Comma Separated Valueの略で、日本語では「カンマ区切りの値」という意味になり、データをカンマで区切った値の事を指します。

id,city,english
1,東京都,Tokyo
2,大阪,Osaka
3,沖縄,Okinawa
4,北海道,Hokkaido

1行目は、2行目以降のデータを取得しやすくするために、データに名前を付けており、CSVファイルを読み込む際にオプションを付ける事によって、

行['データの名前']

と直感的にデータを取得する事ができます。CSVファイルの設定は終了したので実際にexecution.rbファイルでCSVファイルを読み込んでみましょう。

require "csv"
citys=CSV.read("./citys.csv",headers: true)

CSVファイルを読み込む際は、readメソッドを用いてファイルを読み込むます。第1引数にファイル名を第2引数以降は、オプションなどの設定を記述します。

headers: true

上記のheadersオプションをtrueにする事によって、1行目をヘッダ行として扱う事できる様になり、

行['データの名前']

とデータを取得する事ができます。

では実際にデータを取得してみましょう。

 citys.each do |city|
    puts "#{city['id']}.都市名は#{city['city']}で英訳すると#{city['english']}"
  end

以下の様に表示されるはずです。

1.都市名は東京都で英訳するとTokyo
2.都市名は大阪で英訳するとOsaka
3.都市名は沖縄で英訳するとOkinawa
4.都市名は北海道で英訳するとHokkaido

以上で,「CSVファイルの設定」が終了になります。次のステップに進みましょう。

wetherクラスの実装

CSVファイルの準備ができたので、wetherクラスの実装をしていきましょう。

  • initializeメソッドでCSVファイルを読み込もう
  • 都市を選択しよう
  • 天気情報を取得しよう
  • 天気情報を表示しよう
  • 実行しよう

initializeメソッドでCSVファイルを読み込もう

まず、初めにinitializeメソッドを用いてCSVファイルを読み込みましょう。

class Wetaher
  def initialize(citys)
    @citys=citys
  end
 end
  citys=CSV.read("./citys.csv",headers: true)
  wether = Wetaher.new(citys)

CSVファイルをreadメソッドでファイル読み込み、変数citysに戻り値を格納し、wetaherクラスのインスタンスを生成時に引数として変数citysを渡し、初期化します。

都市を選択しよう

Wetaherクラスのインスタンスが生成できたのなら、OpenWeatherMapから天気情報を取得したい都市名を選択するメソッド、「select_cityメソッド」を作成します。

class Wetaher
 def select_city
    @citys.each do |city|
      puts "#{city['id']}:#{city['city']}"
    end
    puts "天気予報を確認したい都市を選択してください"
    input = gets.to_i-1
    @citys[input]
  end
end
city=wether.select_city

インスタンス変数citysの中身は、都市名などが配列上で格納されています。なのでeach文を用いて配列から値を取り出しています。その後、表示された要素のID番号を元に天気情報を取得したい都市名を選択します。

なお、select_cityメソッドを呼び出した際に変数cityに戻り値を受け取っている意図は、単純に次のget_weather_forecastメソッドの引数に渡すためです。

天気情報を取得しよう

天気情報を取得したい都市を選択できたので、OpenWeatherMapから天気情報を取得しましょう。

class Wetaher
  def get_weather_forecast(city)
    response = open(WEATHER_URL + "?q=#{city['english']}&appid=#{WEATHER_API_KEY}&units=metric&lang=ja")
    data=JSON.parse(response.read)
    json_to_parse(data['list'])
    datas= {list: data['list'],city_name:data['city']['name']}
   end
   def json_to_parse(weathers)
    weather=JSON.pretty_generate(weathers)
    File.open("tenki.json", mode = "w"){|f|
      f.write(weather)
    }
   end
end
 datas=wether.get_weather_forecast(city)

まず初めにopenメソッドを用いて、リクエストを送ります。

WEATHER_URL + "?q=#{city['english']}&appid=#{WEATHER_API_KEY}&units=metric&lang=ja"

URLの中身には、色々なオプションを指定しているので、下記のリストで一度、ご確認お願いします。

  • ?q=
    →英語化した都市名が入ります。
  • &appid=
    →あなたのAPIKEYが入ります。
  • &units=metri
    →データ形式の単位を指定します。(metric)
  • &lang=
    →言語を選択します(日本語)。

まあ詳しく詳細を確認したい場合は、OpenWeatherMapのドキュメントを参照してください。

data=JSON.parse(response.read)

変数responseのデータ構造をJsonというデータ構造にパースしています。Jsonにパースする事でデータを取得しやすい形になります。

def json_to_parse(weathers)
    weather=JSON.pretty_generate(weathers)
    File.open("tenki.json", mode = "w"){|f|
      f.write(weather)
    }
   end
json_to_parse(data['list'])

先ほどパースしたデータを格納している、変数dataを引数に渡しjson_to_parseメソッドを呼び出し、tenki.jsonファイルを生成しています。単にJson化したデータ構造をtenki.jsonファイルに書きこんでいるだけです。詳しくは解説はしませんが、後でご自身のテキストエディタで確認をしてみてください。

datas= {list: data['list'],city_name:data['city']['name']}

天気情報を表示する際に、記述量を減らすためにハッシュ化してdatasに格納します。まあ、ハッシュ化したのは、かなりデーター量が多く管理するのが大変だからです。

天気情報を表示しよう

無事、天気情報を取得できたので、データを表示しましょう。

class Wetaher
  def display_weather_forecast(weathers)
      puts "地区:#{weathers[:city_name]}"
      weathers[:list].each do|weather|
        puts "------------------------------"
        puts "時間#{weather['dt_txt']}"
        puts "天気:#{weather['weather'][0]['description']}"
        puts "気温:#{weather['main']['temp']}℃"
        puts "------------------------------"
     end
  end
end
wether.display_weather_forecast(datas)

こちらは、シンプルにeach文を用いてループを回すだけですので特に詳しい解説をしませんが、要するに取得してきた天気情報を表示しているだけです。

  • list
    →天気情報などが格納されています
  • city_name
    →都市名などが格納されています。

実行しよう

最後にプログラムを実行してみましょう。その前に、完成用のコードを貼り付けておきますので、誤差を比べ、エラーが出た場合などに活用してください。

class Wetaher
  def initialize(citys)
    @citys=citys
  end

  def select_city
    @citys.each do |city|
      puts "#{city['id']}:#{city['city']}"
    end
    puts "天気予報を確認したい都市を選択してください"
    input = gets.to_i-1
    @citys[input]
  end

  def get_weather_forecast(city)
    response = open(WEATHER_URL + "?q=#{city['english']}&appid=#{WEATHER_API_KEY}&units=metric&lang=ja")
    data=JSON.parse(response.read)
    json_to_parse(data['list'])
    datas= {list: data['list'],city_name:data['city']['name']}
  end

  def display_weather_forecast(weathers)
    puts "地区:#{weathers[:city_name]}"
    weathers[:list].each do|weather|
      puts "------------------------------"
      puts "時間#{weather['dt_txt']}"
      puts "天気:#{weather['weather'][0]['description']}"
      puts "気温:#{weather['main']['temp']}℃"
      puts "------------------------------"
    end
  end

   def json_to_parse(weathers)
    weather=JSON.pretty_generate(weathers)
    File.open("tenki.json", mode = "w"){|f|
      f.write(weather)
    }
   end

end

def execution
  citys=CSV.read("./citys.csv",headers: true)
  wether = Wetaher.new(citys)
  city=wether.select_city
  datas=wether.get_weather_forecast(city)
  wether.display_weather_forecast(datas)
end
require "./config.rb"
require "json"
require "open-uri"
require "csv"
require "./weather_class.rb"
execution()

それではターミナル上でプログラムを動かしてみましょう。

ruby execution.rb

以下の様に表示されたら、成功です。

まとめ:天気予報APIを用いて5日間の天気情報を取得しよう

今回は、天気予報APIを用いて5日間の天気情報を取得して表示するプログラムを作成してみました。外部と通信する際にAPIキーなどがある場合は、環境変数に格納したりして、外部から値を参照できない様に管理をしましょう。もし漏洩してしまうと大変な事になりますので注意してください。

以上、kazutoでした。