以前作成したオウム返しするLINEボットに、天気予報機能を搭載させていきます。 前回は、使用する天気予報サービスと、モジュールについてまとめました。
- 前回
今回は、天気予報を取得するスクリプトの作成をします。今回作成するスクリプトファイルをLINEボットに読み込ませることで、LINEボットを通して天気予報を取得できるようになります。
目次
詳細設計
まずは、天気予報アプリに必要になる機能をまとめておきます。以下の3つの機能があれば良さそうです。
- 全国地点定義表を読み込み、都市名とそのidを取得
- 入力した都市名が定義表内にあれば天気予報情報を取得
- 天気予報を表示
それぞれの機能に対し、役割に合ったクラス名とメソッドを割り当てておきます。
No. | クラス名 | メソッド |
---|---|---|
1 | GetPlaceData | get_city |
2 | GetWeatherData | get_weather |
3 | GetWeatherData | show_weather |
2つのクラスに分けるのは細かすぎるかもしれませんが、今回はこれで行きます。
全体的なコードの流れは、以下の通りにしていきます。
GetPlaceData
オブジェクトを生成し、get_city
メソッドで全国地点定義表から都市名とそのidを取得- 入力された都市名が全国地点定義表内にあれば
GetWeatherData
オブジェクトを生成し、get_weather
メソッドで天気予報情報を収集 GetWeatherData
オブジェクトのshow_weather
メソッドで、天気予報情報を見やすく表示
早速、上記の設計をもとに、ドラフトとなるコードを書いて保存しておきましょう。ファイル名は、「weathergetter.py」としました。
import requests import bs4 class GetPlaceData(): def __init__(self): pass def get_city(self): pass class GetWeatherData(): def __init__(self): pass def get_weather(self): pass def show_weather(self): pass if __name__ == "__main__": pass
最初に、web上からのデータ収集に必要になるrequests
モジュールと、収集したデータを解析するbs4
モジュールをインポートします。
次に、GetPlaceData
クラスとGetWeatherData
クラスを定義します。
都市データ収集用 GetPlaceDataクラス
このクラスでは、全国地点定義表を読み込み、都市名とそのidを取得する機能を持たせます。
オブジェクト生成時に全国地点定義表を読込み、get_city
メソッドで、都市名をキー、都市idをバリューとした辞書を返すようにします。
コード
class GetPlaceData(): ''' 全国地点定義表から、都市名とidを取得するクラス ''' def __init__(self): ''' 全国地点定義表の読み込み ''' path = 'http://weather.livedoor.com/forecast/rss/primary_area.xml' #1 self.res = requests.get(path) #2 def get_city(self): ''' 都市名とidを辞書型で取得 ''' city_list = {} #3 b = bs4.BeautifulSoup(self.res.text, 'xml').select('city') #4 for city in b: city_list[city.get('title')] = city.get('id') #5 return(city_list) #6
コードの解説
- 全国地点定義表のアドレス
- 全国地点定義表を取得
- 都市名とid保管用に、空の辞書を準備
- BeautifulSoupで、取得した全国地点定義表から「city」タグを収集
- 「city」タグ内から、
.get('title')
で都市名、```.get('id')で都市idを取得し、辞書のキー・バリューとして保管 - 都市名:都市idの辞書を返す
以上で、GetPlaceData
オブジェクトを生成し、get_city
メソッドを実行すれば、都市名と都市idを辞書型で取得できるようになりました。
天気予報データ収集用 GetWeatherDataクラス
このクラスでは、idを入力した都市の天気予報情報を取得する機能を持たせます。
オブジェクト生成時にidを渡してその地点の天気予報情報を読込ませ、get_weather
メソッドで天気、最高気温、最低気温を取得します。
また、show_weather
メソッドで天気予報の結果を出力させます。
コード
class GetWeatherData(): ''' livedoorのAPIから、天気予報データを取得するクラス ''' url = 'http://weather.livedoor.com/forecast/webservice/json/v1' #1 def __init__(self, city_id): place = {'city' : city_id} #2 self.weather_data = requests.get(self.url, place).json() #3 def get_weather(self): self.weather = [] #天気(晴れ、雨など) self.temperature_max = [] #最高気温 self.temperature_min = [] #最低気温 for w in self.weather_data['forecasts']: #4 self.weather.append(w['telop']) #5 try: self.temperature_max.append(' max ' + w['temperature']['max']['celsius'] + '℃ ') #6 except: self.temperature_max.append(' max - ℃ ') try: self.temperature_min.append(' min ' + w['temperature']['min']['celsius'] + '℃ ') #7 except: self.temperature_min.append(' min - ℃ ') def show_weather(self): #8 self.get_weather() date = ['今日 ', '明日 ', '明後日 '] r = '' for i in range(3): try: r = r + date[i] + self.weather[i] + self.temperature_max[i] + self.temperature_min[i] + '\n' except: continue return(r)
コードの解説
- 天気予報APIの基礎アドレス
- 「city」をキー、「都市id」をバリューとした辞書を定義
- 天気予報APIの基礎アドレスに、2の辞書を連結させて都市の天気予報を読み込む(JSON形式)
- 読み込んだ天気予報情報から、「forecasts」キーで3日間の天気予報を抜き取る
- 「telop」キーで天気(晴れ、雨など)を取得
- 「temperature」「max」「celsius」キーで、最高気温を℃で取得(データがない場合は「-」にする)
- 「temperature」「min」「celsius」キーで、最低気温を℃で取得(データがない場合は「-」にする)
get_weather
メソッドを実行し、結果を見やすくして返すメソッド
以上で、GetWeatherData
に都市idを渡してオブジェクトを生成すれば、get_weather
メソッドを用いて天気予報を取得できるようになりました。
動作テスト
最後に、if __name__ == "__main__"
部にテスト用のコードを書いて、実際に動作させてみます。
例として、ユーザーが「函館」と入力したときに天気予報が取得できるか確認します。
コード
if __name__ == "__main__": input_text = '函館' #1 city_data = GetPlaceData() #2 city_dict = city_data.get_city() #3 if input_text in city_dict: #4 r = GetWeatherData(city_dict[input_text]) #5 reply_text = r.show_weather() #6 print(reply_text) #7
コードの解説
- ユーザーの入力。
- GetPlaceDataオブジェクトを生成
- 都市とidを辞書型で取得
- ユーザーの入力した都市が、取得した辞書内にあるか確認
- GetWeatherDataオブジェクトを生成
- 入力した都市の天気予報情報を取得
- 天気予報を表示
完成したコードを実行して動作確認をしてみましょう。
事前にpipenv shell
を実行して仮想環境に入った後、python weathergetter.py
を実行します。
以下の様な結果が出力されれば成功です。当たり前ですが、天気予報取得のタイミングで天気予報と気温は変化します。
今日 晴のち曇 max - ℃ min - ℃ 明日 曇のち晴 max 6℃ min 0℃ 明後日 晴時々曇 max - ℃ min - ℃
次は、Herokuへデプロイしてアプリを完成させます。
コード全文
import bs4 import requests class GetPlaceData(): ''' 全国の地点定義表から、都市名とidを取得するクラス ''' def __init__(self): ''' 全国地点定義表を読み込む ''' path = 'http://weather.livedoor.com/forecast/rss/primary_area.xml' self.res = requests.get(path) def get_city(self): ''' 都市名とidを辞書型で取得 ''' city_list = {} b = bs4.BeautifulSoup(self.res.text, 'xml').select('city') for city in b: city_list[city.get('title')] = city.get('id') return(city_list) class GetWeatherData(): ''' livedoorのAPIから、天気予報データを取得するクラス ''' url = 'http://weather.livedoor.com/forecast/webservice/json/v1' def __init__(self, city_id): place = {'city' : city_id} self.weather_data = requests.get(self.url, place).json() def get_weather(self): self.weather = [] #天気(晴れ、雨など) self.temperature_max = [] #最高気温 self.temperature_min = [] #最低気温 for w in self.weather_data['forecasts']: self.weather.append(w['telop']) try: self.temperature_max.append(' max ' + w['temperature']['max']['celsius'] + '℃ ') except: self.temperature_max.append(' max - ℃ ') try: self.temperature_min.append(' min ' + w['temperature']['min']['celsius'] + '℃ ') except: self.temperature_min.append(' min - ℃ ') def show_weather(self): self.get_weather() date = ['今日 ', '明日 ', '明後日 '] r = '' for i in range(3): try: r = r + date[i] + self.weather[i] + self.temperature_max[i] + self.temperature_min[i] + '\n' except: continue return(r) if __name__ == "__main__": input_text = '函館' city_data = GetPlaceData() city_dict = city_data.get_city() if input_text in city_dict: r = GetWeatherData(city_dict[input_text]) reply_text = r.show_weather() print(reply_text)