7
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

天候・気温予測サービス "OpenWeather" のAPIを活用する

Last updated at Posted at 2023-06-29

「Python2年生 スクレイピングのしくみ」という本にOpenWeatherという天候・気温予測サービスの使い方が紹介されていたので紹介します。

豪雨災害・台風災害の対策に役立てたり、植え付け・収穫時期の調整といった農業に役立てることで労働生産性を改善できるサービスなのではないかと期待しています。

APIを活用する

API(アプリケーションプログラミングインタフェース、 Application Programming Interface)とはソフトウェアがお互いにやり取りするためのシステムやフレームワークである、と大雑把に理解しています。例えばコンセントの材質や誘電率などを知らなくても家電をコンセントにつなげばいつでも様々な家電を使うことができます。電気インフラとコンセント・家電はこのようなシステムの関係になっているのですが、ソフトウェア開発ではAPIが電機システムにおけるコンセントの役割をします。

今(当日)の天候を取得する

OpenWeatherに登録すると無償で1分60回APIを呼び出せるようになります(APIの有効化には数時間ほどかかります)。 例えば都市名ごとの気象情報のjsonファイルをAPIから取得するには

https://api.openweathermap.org/data/2.5/weather?q={city_name}&appid={API_key}

このリンクの{city_name}を都市名に、{API_key}を取得したAPIキーにPython経由でrequests.getでアクセスしてください。またデフォルトがだと気温が華氏で取得されます。摂氏を基準としたい場合はクエリに units=metric を含めます。

import requests
import json
from pprint import pprint

url = "https://api.openweathermap.org/data/2.5/weather?q={city_name}&units=metric&appid={API_key}"
# xxxxxには取得したAPIキーを入れる
url = url.format(city_name = "Tokyo", API_key = "xxxxx")

jsondata = requests.get(url).json()
pprint(jsondata)
{'base': 'stations',
 'clouds': {'all': 75},
 'cod': 200,
 'coord': {'lat': 35.6895, 'lon': 139.6917},
 'dt': 1687917481,
 'id': 1850144,
 'main': {'feels_like': 35.33,
          'humidity': 66,
          'pressure': 1010,
          'temp': 30.54,
          'temp_max': 32.55,
          'temp_min': 27.75},
 'name': 'Tokyo',
 'sys': {'country': 'JP',
         'id': 2001249,
         'sunrise': 1687894045,
         'sunset': 1687946462,
         'type': 2},
 'timezone': 32400,
 'visibility': 8000,
 'weather': [{'description': 'broken clouds',
              'icon': '04d',
              'id': 803,
              'main': 'Clouds'}],
 'wind': {'deg': 180, 'speed': 5.14}}

取得したデータには、このようなキーと値が含まれています。

print("天気:",jsondata["weather"][0]["main"])
print("天気詳細:",jsondata["weather"][0]["description"])

print("都市名:",jsondata["name"])
print("気温:",jsondata["main"]["temp"])
print("体感気温:",jsondata["main"]["feels_like"])
print("最低気温:",jsondata["main"]["temp_min"])
print("最高気温:",jsondata["main"]["temp_max"])
print("気圧:",jsondata["main"]["pressure"])
print("湿度:",jsondata["main"]["humidity"])

print("風速:",jsondata["wind"]["speed"])
print("風の方角:",jsondata["wind"]["deg"])
天気: Clouds
天気詳細: broken clouds
都市名: Tokyo
気温: 29.84
体感気温: 34.42
最低気温: 27.75
最高気温: 31
気圧: 1010
湿度: 69
風速: 4.63
風の方角: 170

郵便番号のある座標の天候

url = "https://api.openweathermap.org/data/2.5/weather?zip={zip_place}&units=metric&appid={API_key}"
# xxxxx
url = url.format(zip_place = "110-0006,JP", API_key = "xxxxx")

jsondata = requests.get(url).json()
pprint(jsondata)

print("天気:",jsondata["weather"][0]["main"])
print("天気詳細:",jsondata["weather"][0]["description"])

print("都市名:",jsondata["name"])
print("気温:",jsondata["main"]["temp"])
print("体感気温:",jsondata["main"]["feels_like"])
print("最低気温:",jsondata["main"]["temp_min"])
print("最高気温:",jsondata["main"]["temp_max"])
print("気圧:",jsondata["main"]["pressure"])
print("湿度:",jsondata["main"]["humidity"])

print("風速:",jsondata["wind"]["speed"])
print("風の方角:",jsondata["wind"]["deg"])
{'base': 'stations',
 'clouds': {'all': 75},
 'cod': 200,
 'coord': {'lat': 35.7022, 'lon': 139.7749},
 'dt': 1687919279,
 'id': 0,
 'main': {'feels_like': 36.4,
          'humidity': 64,
          'pressure': 1010,
          'temp': 31.22,
          'temp_max': 32.29,
          'temp_min': 28.97},
 'name': 'Akihabara',
 'sys': {'country': 'JP',
         'id': 268395,
         'sunrise': 1687894022,
         'sunset': 1687946445,
         'type': 2},
 'timezone': 32400,
 'visibility': 10000,
 'weather': [{'description': 'broken clouds',
              'icon': '04d',
              'id': 803,
              'main': 'Clouds'}],
 'wind': {'deg': 170, 'speed': 5.14}}
天気: Clouds
天気詳細: broken clouds
都市名: Akihabara
気温: 31.22
体感気温: 36.4
最低気温: 28.97
最高気温: 32.29
気圧: 1010
湿度: 64
風速: 5.14
風の方角: 170

JSONデータの書き出し

取得したJSONデータを書きだす・読みめるようにしておくとデータの管理に便利です。

書き出し

from pathlib import Path
#JSONデータの書き出し

# JSONはバイナリモードでファイルに書き込みする。直接オブジェクトをファイルに出力することはできない
with open("20230628_weather_akihabara.json", "wb") as outfile:
    json_str = json.dumps(jsondata)
    json_bin = json_str.encode("utf-8")
    outfile.write(json_bin)

"""
with open("20230628_weather_akihabara.json", "wb") as outfile:
    outfile.write(jsondata)
"""

読み込み

#JSONデータの読み込み
with open("20230628_weather_akihabara.json", "r") as readfile:
    jsondata = json.loads(readfile.read())
    pprint(jsondata)

5日間、3時間ごとの天候を予測する

OpenWeatherでは天候の予測情報も取得できます。当日の天候にはクエリにweather、予測情報の取得にはforecastを用います。58なので40回分の予測データが入手できます。データは"list"下の階層に格納されています。

import requests
import json
from pprint import pprint

url = "https://api.openweathermap.org/data/2.5/forecast?q={city_name}&units=metric&appid={API_key}"
# xxxxxには取得したAPIキーを入れる
url = url.format(city_name = "Tokyo", API_key = "xxxxx")

jsondata = requests.get(url).json()
pprint(jsondata)
{'city': {'coord': {'lat': 35.6895, 'lon': 139.6917},
          'country': 'JP',
          'id': 1850144,
          'name': 'Tokyo',
          'population': 12445327,
          'sunrise': 1687894045,
          'sunset': 1687946462,
          'timezone': 32400},
 'cnt': 40,
 'cod': '200',
 'list': [{'clouds': {'all': 75},
           'dt': 1687921200,
           'dt_txt': '2023-06-28 03:00:00',
           'main': {'feels_like': 36.3,
                    'grnd_level': 1005,
                    'humidity': 65,
                    'pressure': 1010,
                    'sea_level': 1010,
                    'temp': 31.06,
                    'temp_kf': 1.27,
                    'temp_max': 31.06,
                    'temp_min': 29.79},
           'pop': 0.03,
           'sys': {'pod': 'd'},
           'visibility': 10000,
           'weather': [{'description': 'broken clouds',
                        'icon': '04d',
                        'id': 803,
                        'main': 'Clouds'}],
           'wind': {'deg': 168, 'gust': 4.5, 'speed': 4.82}},
(以下40日分)

"list" "dt"dt_text"にはタイムスタンプと時刻が含まれていますが、これはUTC(協定世界時)となっているのでJST(日本標準時)に変換します。
変換にはdatetimeのタイムスタンプを変換するための機能を使います。

#UTCをJSTに変換するためのタイムスタンプの変換を定義する
from datetime import datetime, timedelta, timezone
timestamp = 1687921200

tz = timezone(timedelta(), "UTC")
utc = datetime.fromtimestamp(timestamp, tz)
print(utc)

tz = timezone(timedelta(hours=+9), "JST")
jst = datetime.fromtimestamp(timestamp, tz)
print(jst)
2023-06-28 03:00:00+00:00
2023-06-28 12:00:00+09:00

forecastのjsonデータすべてをJSTのタイムスタンプに変換するには、このように記述します。

import requests
import json
from pprint import pprint
from datetime import datetime, timedelta, timezone

#forecastデータ
# xxxxxには取得したAPIキーを入れる
url = "https://api.openweathermap.org/data/2.5/forecast?q={city_name}&units=metric&appid={API_key}"
url = url.format(city_name = "Tokyo", API_key = "xxxxx")
jsondata = requests.get(url).json()
pprint(jsondata)

#タイムスタンプを変換
tz = timezone(timedelta(hours=+9), "JST")
for dat in jsondata["list"]:
    jst = str(datetime.fromtimestamp(dat["dt"], tz))[:-9]
    print("UST={ust}, JST={jst}".format(ust=dat["dt_txt"], jst=jst))
{'city': {'coord': {'lat': 35.6895, 'lon': 139.6917},
          'country': 'JP',
          'id': 1850144,
          'name': 'Tokyo',
          'population': 12445327,
          'sunrise': 1687894045,
          'sunset': 1687946462,
          'timezone': 32400},
 'cnt': 40,
 'cod': '200',
 'list': [{'clouds': {'all': 83},
           'dt': 1687932000,
           'dt_txt': '2023-06-28 06:00:00',
           'main': {'feels_like': 36.22,
                    'grnd_level': 1003,
                    'humidity': 62,
                    'pressure': 1008,
                    'sea_level': 1008,
                    'temp': 31.38,
                    'temp_kf': 1.03,
                    'temp_max': 31.38,
                    'temp_min': 30.35},
           'pop': 0.08,
           'sys': {'pod': 'd'},
           'visibility': 10000,
           'weather': [{'description': 'broken clouds',
                        'icon': '04d',
                        'id': 803,
                        'main': 'Clouds'}],
           'wind': {'deg': 166, 'gust': 4.82, 'speed': 4.47}}
(以下40日分)

5日間の気温を折れ線グラフに

5日先の秋葉原の気温を折れ線グラフにし表示します。

import requests
import json
from pprint import pprint
from datetime import datetime, timedelta, timezone

import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib

#forecastデータ
url = "https://api.openweathermap.org/data/2.5/forecast?q={city_name}&units=metric&appid={API_key}"
# xxxxxには取得したAPIキーを入れる
url = url.format(city_name = "Tokyo", API_key = "xxxxx")
jsondata = requests.get(url).json()

#データフレーム
df = pd.DataFrame(columns=["気温","体感温度"])

#タイムスタンプを変換
tz = timezone(timedelta(hours=+9), "JST")
for dat in jsondata["list"]:
    jst = str(datetime.fromtimestamp(dat["dt"], tz))[:-9]
    #print("UST={ust}, JST={jst}".format(ust=dat["dt_txt"], jst=jst))
    
    temp = dat["main"]["temp"]
    feels_like = dat["main"]["feels_like"]
    df.loc[jst] = [temp,feels_like]
    #df.loc[jst] = temp

df.plot(figsize=(15,8))
plt.ylim(-10, 40)
plt.grid()
plt.show()

download.png
予測によると、秋葉原のこれから先5日間の気温は大きく上昇することはないようです。これに加え天候・風速なども合わせてデータとすると非常に有用な予測となります。

有償プランで30日の天候気温などが予測できれば災害対策や農業の労働生産性を改善になる素晴らしいシステムが実現できるのではないかと考えました。

7
13
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?