Pythonロボティクスコース レッスン 43
テーマ.11-2 インターネットを利用した気象予報データの取得
インターネットのWebサイトから情報を取得しよう
チャプター1
このレッスンで学ぶこと
このレッスンでは、「世界気象機関(World Meteorological Organization:WMO)」から配信されている世界の気象予報のデータを取得して、Studuino:bitのLEDディスプレイに表示するプログラムを作成します。
チャプター2
HTTPのPOSTメソッド
前回のレッスンでは、HTTPの仕組みやHTTPで扱うメソッドについて紹介し、実際にGETメソッドを使用してインターネットから情報を取得する練習を行いました。ここでは、GETメソッドと並んでよく使用される「POSTメソッド」を実践してみましょう。
2. 1 GETメソッドとPOSTメソッドの違い
HTTPのGETメソッドとPOSTメソッドは、それぞれ以下の目的で使うことを説明しました。
- GET
リソースを取得するメソッド。GETメソッドを実行することで、サーバーのリソースが変更されることはありません。 - POST
データを送信するメソッド。サーバーは受信したデータを元に、リソースを書き換えたり、新たなリソースを作成したりして、レスポンスを送ります。
基本はGETメソッドはサーバーからのリソース取得、POSTメソッドはサーバーへのデータ送信を行うときに使われます。ただし、GETメソッドでもURLにクエリパラメータを含めることで、サーバーへデータを送信できました。
【 クエリパラメータを含んだURLの例 】
※ 末尾にある?
以降のcolor=red
がクエリパラメータ
https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color=red
POSTメソッドを利用する代表例は、Webサイトのフォームデータの送信です。
送信するフォームデータは、リクエストのボディに含まれています。
サーバーはPOSTメソッドでリクエストが送られてきた場合はボディを確認し、その内容に合わせてリソースを更新したり、新たなWebページを作成してクライアントへレスポンスを返します。
2. 2 POSTメソッドの実践
では実際に、POSTメソッドを使ってサーバーにデータを送信してみましょう。練習用にWebサイトのログインフォームをイメージした以下のサンプルページを用意しています。
https://www.artec-kk.co.jp/school/cl/textbooks/sample/post
このページをそのままブラウザで開くと、次のメッセージが表示されます。
これはGETメソッドでリクエストを送っているためです。このページへはPOSTメソッドでid
とpassword
の2つのパラメータを送る必要があります。
前のレッスンで作成したWi-Fi接続用のモジュール(mywifi
)とurequests
モジュールを使ってリクエストを送信し、そのレスポンスを表示するプログラムを作成しましょう。
■ プログラムの作成手順
始めにStuduino:bitをWi-Fiに接続します。前のレッスンを思い出しながら、以下のコードを書きましょう。
import urequests
from mywifi import MyWifi
SSID = "SSID" # 教室で使用するWi-FiのアクセスポイントのSSID
PASSWORD = "PASSWORD" # 教室で使用するWi-Fiのアクセスポイントのパスワード
URL = "https://www.artec-kk.co.jp/school/cl/textbooks/sample/post/" # リクエスト送信先のURL
wifi = MyWifi() # 前のレッスンで作成した独自クラスのオブジェクトの作成
def login(_id, _pass): # 認証用のIDとパスワードを引数に指定
if not wifi.isconnected(): # Wi-Fi接続が完了していない場合のみ接続を試みる
if not wifi.connect(SSID, PASSWORD):
return # 接続に失敗した場合はここで処理を終える
POSTメソッドでリクエストを送るときは、urequests
モジュールのpost()
関数を使います。
urequests.post(url, data=None, header={})
GETメソッドでリクエストを送ったときは、リクエストのボディ(data
)とヘッダ(header
)を指定しませんでしたが、今回はそれらが必要になります。
まず、ボディには次の形式で送信したいデータを設定します。
id=....&password=....
この形式はクエリパラメータと同様で「変数名=値」でデータを表し、複数のパラメータがあるときは「&」でつなぎます。
ヘッダではボディのデータ形式をContent-Type
ヘッダで示します。もしこれがなければ、リクエストを受け取ったサーバーは、送られてきたデータをどのように解釈すれば良いのか分かりません。
Content-Type: application/x-www-form-urlencoded
application/x-www-form-urlencoded
は、変数名と値が間にある=
で組み合わされており、情報が複数ある場合は&
で区切られていることを示しています。フォームに入力したデータを送信するときは、基本的にこのヘッダ情報が指定されます。
これらのことを踏まえた上で以下のコードを追加して、レスポンスの内容をターミナルに表示しましょう。
【 サンプルコード 2-2-1 】
追加【13行目~16行目】
import urequests
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
URL = "https://www.artec-kk.co.jp/school/cl/textbooks/sample/post/"
wifi = MyWifi()
def login(_id, _pass):
if not wifi.isconnected():
if not wifi.connect(SSID, PASSWORD):
return
_data = "id={}&password={}".format(_id, _pass) # リクエストボディの情報
_headers = {"Content-Type": "application/x-www-form-urlencoded"} # リクエストヘッダの情報
res = urequests.post(URL, data=_data, headers=_headers) # POSTメソッドでリクエストを送信
print(res.text) # レスポンスの内容を表示
リクエストを送るWebページの認証に必要なIDとパスワードの組み合わせは次の通りです。
- ID:python
- パスワード:robotics
完成したプログラムを実行して、login()
関数を呼び出してみましょう。
(正しいIDとパスワードの組み合わせを送信したときの結果)
>>> login("python", "robotics") Login succeeded. Welcome to the Python Robotics Course!
(誤ったIDとパスワードの組み合わせを送信したときの結果)
>>> login("ruby", "robotics") Login failed. The ID or password is incorrect.
このように受け取ったデータに応じて、サーバーは異なるレスポンスを返すようになっています。今回利用したWebページでは簡単なメッセージだけを返すようにしていましたが、実際のフォームでは入力されたメールアドレス宛てに受け付け確認メールを送信したり、フォームの情報をデータベースに登録したりする処理が行われています。
チャプター3
JSON(データ形式)
サーバーから返されるレスポンスのデータは、先ほどのような単純なテキストだけでなく、HTMLの文書やJavaScriptというWebブラウザで動作するプログラム言語で書かれたプログラムファイル、画像、音声、動画など様々です。
ここでは、その中から後の気象予報のデータ取得でも利用する「JSON(ジェイソン)」というデータについて紹介します。
3. 1 JSONとは
JSONは「JavaScript Object Notation」の略で、元々はJavaScriptというプログラム言語で扱うオブジェクトをテキスト形式で表現するために開発されたデータフォーマットの1種です。今ではWebのアプリケーションどうしでデータをやり取りするときの標準的なフォーマットのひとつとして使われています。
■ JSONのフォーマット
JSONのフォーマットは次の通りです。Pythonでいう複数の辞書型のデータが入れ子になった構造をしています。そのため、JSONはPythonでも簡単に扱うことができます。
{ "data1": "string1", "data2": 2, "data3": { "data3_1": 3, "data3_2": "string3", } }
3. 2 JSONの実践
それでは実際に以下のWebページからJSONのデータを取得してみましょう。
https://www.artec-kk.co.jp/school/cl/textbooks/sample/json
このページにWebブラウザでアクセスすると以下のようなJSONのデータが表示されます。
{"method":"show","args":{"string":"JSON","delay":400,"wait":false,"loop":false,"clear":true,"color":2031616}}
このままでは構造が分かりにくいので、整列してみましょう。
{ "method": "show", "args": { "string": "JSON", "delay": 400, "wait": false, "loop": false, "clear": true, "color": 2031616 } }
実は、これらの情報はStuduino:bitのLEDディスプレイの制御方法を示していて、各パラメータは以下の情報を表しています。
パラメータ | 内容 |
---|---|
"method" | LEDディスプレイの制御メソッドの名前。リクエストを送るごとに"show" または"scroll のいずれかがランダムに選ばれます。 |
"args" | "method" に合わせた以下の引数情報。 |
【 “method”: “scroll” の場合の”args”のパラメータ 】
パラメータ | 内容 |
---|---|
"string" | scroll() メソッドの第1引数に対応。”JSON”で固定。 |
"delay" | scroll() メソッドのdelay 引数に対応。10で固定。 |
"wait" | scroll() メソッドのwait 引数に対応。falseで固定。 |
"loop" | scroll() メソッドのloop 引数に対応。falseで固定。 |
"color" | scroll() メソッドのcolor 引数に対応。7色からランダムに選ばれる。 |
【 “method”: “show” の場合の”args”のパラメータ 】
パラメータ | 内容 |
---|---|
"string" | show() メソッドの第1引数に対応。”JSON”で固定。 |
"delay" | show() メソッドのdelay 引数に対応。400で固定。 |
"wait" | show() メソッドのwait 引数に対応。falseで固定。 |
"loop" | show() メソッドのloop 引数に対応。falseで固定。 |
"clear" | show() メソッドのclear 引数に対応。trueで固定。 |
"color" | show() メソッドのcolor 引数に対応。7色からランダムに選ばれる。 |
いくつかのパラメータは、サーバー側で値がランダムに選択されるようになっています。
それでは取得したJSONデータの内容に合わせて、LEDディスプレイを制御するプログラムを作成してみましょう。
■ プログラムの作成手順
Studuino:bitをWi-Fiに接続するコードを書きます。
import urequests
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
wifi = MyWifi()
def control_display():
if not wifi.isconnected():
if not wifi.connect(SSID, PASSWORD):
return
今回は送信するデータがないので、GETメソッドでリクエストを送ります。サーバーから送られてきたデータは、urequests
モジュールのget()
メソッドで返されるresponse
オブジェクトのcontent
プロパティに格納されています。そして、response
オブジェクトがもつjson()
メソッドを実行すると、content
がJSONとして解釈されて辞書型のデータが得られます。
以下のようにコードを追加して、ここまでのプログラムを実行してみましょう。
追加【6行目、13行目~16行目】
import urequests
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
URL = "https://www.artec-kk.co.jp/school/cl/textbooks/sample/json/" # リクエストの送信先
wifi = MyWifi()
def control_display():
if not wifi.isconnected():
if not wifi.connect(SSID, PASSWORD):
return
res = urequests.get(URL) # リクエストの送信とレスポンスの受信
data = res.json() # JSONのデータをPythonの辞書型のデータに変換
print(data) # 取得したデータを表示する
print(type(data)) # JSONから辞書型のデータに変換されていることを確認
(実行結果の例)
>>> control_display() . . Connected. ('172.20.10.4', '255.255.255.240', '172.20.10.1', '172.20.10.1') {'method': 'scroll', 'args': {'delay': 10, 'color': 2031647, 'string': 'JSON', 'wait': False, 'loop': False}} <class 'dict'>
一番下の行を見ると、<class 'dict'>
が出力されていて、JSONのデータが辞書型のデータに変換できていることが分かります。
では、このデータを使ってLEDディスプレイを制御するコードを書きましょう。
【 サンプルコード 3-2-1 】
追加【18行目~32行目】
import urequests
from pystubit.board import display # LEDディスプレイの制御用オブジェクトのインポート
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
URL = "https://www.artec-kk.co.jp/school/cl/textbooks/sample/json/"
wifi = MyWifi()
def control_display():
if not wifi.isconnected():
if not wifi.connect(SSID, PASSWORD):
return
res = urequests.get(URL)
data = res.json()
# print(data) # 必要がなければコメントアウトする
# print(type(data))
if data["method"] == "scroll": # スクロール表示するとき
args = data["args"]
display.scroll(args["string"],
delay=args["delay"],
wait=args["wait"],
loop=args["loop"],
color=args["color"])
elif data["method"] == "show": # 1文字ずつ表示するとき
args = data["args"]
display.show(args["string"],
delay=args["delay"],
wait=args["wait"],
loop=args["loop"],
clear=args["clear"],
color=args["color"])
これでプログラムの完成です。ターミナルからcontrol_display()
関数を呼び出すたびに、「JSON」の4文字がランダムな色でLEDディスプレイに表示されることを確認しましょう。
チャプター4
気象予報データの取得
政府機関や公益性のある機関のWebサイトでは、地域の人口の推移や人口における年齢比率、交通情報、災害情報、郵便番号、気象予報など様々な情報が無料で公開されています。
レッスンの後半では、国際連合の専門機関のひとつでもある「世界気象機関(World Meteorological Organization:通称WMO)」のWebサイトで公開されている加盟国の気象予報データを取得して、LEDディスプレイに結果を表示するプログラムを作成します。
【 世界気象機関 (World Meteorological Organization)とは 】
国際連合の専門機関の一つで、気象事業の国際的な標準化と改善および調整、 並びに各加盟国・地域間における気象情報・資料の効率的な交換の奨励を主な業務としている。 本部はスイスのジュネーヴにあり、国連開発グループ(UNDG)の一員である。
(引用:wkipedia「世界気象機関」)
4. 1 気象予報データの取得方法
WMOの加盟国の気象データの取得方法については、以下のページ(英語)にまとめられています。
https://worldweather.wmo.int/en/dataguide.html
このページに沿って手順を説明すると、最初に以下のURLにGETメソッドでリクエストを送ります。
https://worldweather.wmo.int/en/json/[City ID]_en.json
このURL内の[City ID]
には、各国の都市に割り当てられている固有の番号を入力します。各都市の番号は以下のページで確認することができます。
https://worldweather.wmo.int/en/json/full_city_list.txt
例えば日本の場合は、以下の7都市が登録されていて、それぞれ次の[City ID]
が割り当てられています。
【 日本の各都市の”City ID” 】
都市名 | [City ID] |
---|---|
札幌("Sapporo" ) | 181 |
仙台("Sendai" ) | 182 |
東京("Tokyo" ) | 183 |
名古屋("Nagoya" ) | 355 |
大阪("Osaka" ) | 184 |
福岡("Fukuoka" ) | 185 |
那覇("Naha" ) | 186 |
リクエストを送ると、JSONでまとめられた気象予報に関するデータが返ってきます。このデータは入れ子構造をもつ辞書型のオブジェクトを表していて、一番上の階層にcity
という名前のキーがあります。
{ "city":{} }
キー名 | 内容 | データ型 |
---|---|---|
"city" | 対象地域の気象予報データ | 辞書型のオブジェクト |
"city"
の値も辞書型のオブジェクトになっていて、複数のキーを持っています。
【 “city”の内容例 】
"city":{ "lang":"en", "cityName":"Tokyo", "cityLatitude":"35.680000000", "cityLongitude":"139.770000000", "cityId":183, "isCapital":true, "stationName":"Tokyo", "tourismURL":"www.jnto.go.jp", "tourismBoardName":"Japan National Tourist Organization", "isDep":false, "timeZone":"+0900", "isDST":"N", "member":{}, "forecast":{}, "climate":{} }
キー名 | 内容 | データ型 |
---|---|---|
"lang" | 言語 | 文字列 |
"cityName" | 都市名 | 文字列 |
"cityLatitude" | 緯度 | 文字列 |
"citiLongitude" | 経度 | 文字列 |
"cityId" | 都市のID | 整数 |
"isCapital" | 首都かどうか | ブール値 |
"station Name" | 駅名、空港名 | 文字列 |
"tourismURL" | 観光局のURL | 文字列 |
"tourismBoardName" | 観光局名 | 文字列 |
"isDep" | 政治的に完全に独立していない地域かどうか | ブール値 |
"timeZone" | タイムゾーン | 文字列 |
"isDST" | 都市がサマータイムにあるかどうか | 文字列 |
"member" | 世界気象機関の組織メンバーとしての情報 | 辞書型のオブジェクト |
"forecast" | 気象予報のデータ | 辞書型のオブジェクト |
"climate" | 気候に関するデータ | 辞書型のオブジェクト |
"member"
や"forecast"
、"climate"
の値も辞書型のオブジェクトです。例えば"forecast"
は次のキーを持っています。
【 “forecast”の内容例 】
"forecast":{ "issueDate":"2020-07-28 11:00:00", "timeZone":"Local", "forecastDay":[] }
キー名 | 内容 | データ型 |
---|---|---|
"issueDate" | 交付日時 | 文字列 |
"timeZone" | 表示されている日付のタイムゾーン | 文字列 |
"forecastDay" | 気象予報の全データ | リスト型のオブジェクト |
"forecastDay"
はリスト型のオブジェクトになっており、[0]~[6]
に明日以降※の1週間分の気象予報データが格納されています。そして、このリストの各要素は以下のキーを中に含んでいます。
※ 取得する時間が早い場合、取得した日から1週間分の気象予報データが格納されています。
【 “forecastDay”の内容例 】
"forecastDay":[ { "forecastDate":"2020-07-29", "wxdesc":"", "weather":"Rain", "minTemp":"24", "maxTemp":"27", "minTempF":"75", "maxTempF":"81", "weatherIcon":1401 }, . . . ]
キー名 | 内容 | データ型 |
---|---|---|
"forecastDate" | 予報日 | 文字列 |
"wxdesc" | 詳細に記述された気象情報 | 文字列 |
"weather" | 気象 | 文字列 |
"minTemp" | 最低気温(セ氏温度) | 数値(小数点以下あり) |
"maxTemp" | 最高気温(セ氏温度) | 数値(小数点以下あり) |
"minTempF" | 最低気温(華氏温度) | 数値(小数点以下あり) |
"maxTempF" | 最高気温(華氏温度) | 数値(小数点以下あり) |
"weatherIcon" | 気象に関するアイコンを表す番号/アイコンの一覧表はこちら | 整数値 |
今回は利用しませんが、"member"
や"climate"
の内容についても詳しく知りたい場合は、このJSONフォーマットを説明している以下のページ(英語)を参照してください。
http://worldweather.wmo.int/en/json/WWIS_json_schema_v2.json
以上のことから、例えば明日の気象を知りたい場合は、"city"
⇒ "forecast"
⇒ "forecastDay"
⇒ [0]
⇒ "weather"
とキーを辿れば良いということになります。
という訳で、"weather"
を確認すれば気象が分かりますが、実は気象を表す単語はかなりの数があります。以下の表はその一例です。
【 気象を表す単語の一例 】
気象名(英語) | 気象名(日本語) | 表す天気の様子 |
---|---|---|
Sandstorm | 砂嵐 | 激しい風によって、塵(ちり)や砂が空中高く吹き上げられるもの |
Duststorm | 砂塵嵐 | 砂嵐と同じだが、吹き上げられる粒子がより細かい |
Sand | 砂 | 大気中に塵や砂が舞っている状態 |
Dust | 塵 | 大気中に塵や砂が舞っている状態(Sandより粒子がより細かい) |
Thunderstorms | 雷雨 | 激しい雨や風を伴う雷や稲妻からなる嵐 |
Thundershowers | 雷雨 | Thunderstormsより短い時間で起きる雷雨 |
Storm | 暴風雨 | 強い風を伴う雨 |
Lightning | 稲妻 | 雷や雷によって発生する光 |
Hail | 雹 | 積乱雲から降る直径5mm以上の氷の粒 |
Blowing Snow | 吹雪 | 地表に積もった雪が強風で吹き上がる様 |
Blizzard | 猛吹雪 | 極地方などで発生する激しい吹雪 |
Snowdrift | 雪の吹きだまり | 降った雪が堆積したもの |
Snowstorm | 吹雪 | 強風によって雪が激しく乱れ飛びながら降る様 |
Snow Showers | にわか雪 | 一時的に強く雪が降る様 |
Flurries | 小雪 | こまかに雪が降る様 |
Snow | 雪 | 大気中の水蒸気から生成される氷の結晶が空から落下してくる天気 |
Heavy Snow | 豪雪 | 雪による雪害のうち、程度の著しいもの |
Snowfall | 降雪 | 雪が降ってくる天気 |
Light Snow | 泡雪 | あわのように軽くてとけやすい雪 |
Showers | にわか雨 | 一時的に降る雨 |
Heavy Showers | 豪雨 | 一時的に激しく雨が降る |
Rainshower | しゅう雨 | 降水強度が急に変化し、降り始めから降り止みが突然で、連続・集中せずに何度も間をおいて降るような雨 |
Occasional Showers | 時々雨 | 一定の予報期間において降ったりやんだりする雨 |
Scattered Showers | にわか雨 | 一時的に降る雨 |
Isolated Showers | 局所的なにわか雨 | 局所的かつ一時的に降る雨 |
Light Showers | よわいにわか雨 | より降水強度の弱いにわか雨 |
Freezing Rain | 凍雨 | 透明あるいは半透明の氷が雨のように降る |
Rain | 雨 | 上空の水蒸気が、高所で凝結し、水滴となって落ちてくる天気 |
Drizzle | 霧雨 | 粒子の細かい雨 |
Light Rain | 小雨 | 降水量の少ない雨 |
Fog | 霧 | 細かい水滴が地面・水面近くに集まって煙のようにかかる現象 |
Mist | 靄(もや) | 霧と同様の減少だが霧よりも薄いものを指す |
Smoke | 煙 | 森林火災などの影響で煙が空気中にあることを指す |
Haze | 煙霧 | 乾いた微粒子が大気中に浮遊している現象 |
Overcast | 曇り | Cloudyよりも空一面がどんよりとした雲で覆われている様 |
Sunny Intervals | 晴れ間 | 雨や曇りの間にときどき見える晴れ間 |
Clearing | 晴れ間 | 雨や曇りの間にときどき見える晴れ間 |
Sunny Periods | 晴れ間 | 雨や曇りの間にときどき見える晴れ間 |
Partly Cloudy | ときどき曇り | 晴れよりも曇りの時間が短い(2分の1未満) |
Partly Bright | ときどき晴れ | 曇りよりも晴れの時間が短い(2分の1未満) |
Mild | のどかな気候 | のどかで穏やかな過ごしやすい天気 |
Cloudy | くもり | 空が雲で覆われている様子 |
Mostly Cloudy | ほとんど曇り | ほとんどの時間空がクモで覆われている様子 |
Bright | 晴れ | よく晴れた様子 |
Sunny | 晴れ | 太陽の光が照っている様 |
Fine | 晴れ | 過ごしやすい天候 |
Fair | 快晴 | 雲が非常に少なく晴れた日 |
Clear | 快晴 | 雲ひとつ無いような晴れた日 |
Windy | 強風 | 風が強い日 |
Squall | 激しい天候変化 | 豪雨や雷雨などを伴う激しい天候の変化 |
Stormy | 暴風/暴雨 | 雨を伴うような激しい強風 |
Freezing | 非常に寒い | 凍えるような寒さ |
Volcanic Ash | 火山灰 | 火山灰が降る |
地域によっては滅多に使われない気象名もあるでしょう。もし、取得した気象名がどのような天気の様子を表しているのか分からないときは、上の表で確認しましょう。
4. 2 プログラムの作成
それでは、気象予報データを取得して、ターミナルに明日(時間によっては今日)の「気象」「最高気温」「最低気温」を表示するプログラムを作成してみましょう。
■ Wi-Fiへの接続
気象予報データを取得する処理はget_forecast()
関数としてまとめます。同じ関数で様々な都市に対応できるように、都市を表す固有番号を引数として受け取ります。Wi-Fiに未接続の場合はこの関数の冒頭で接続するようにしておきましょう。
import urequests
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
wifi = MyWifi()
def get_forecast(city_id): # 引数として都市を表す固有の番号を受け取る
if not wifi.isconnected():
if not wifi.connect(SSID, PASSWORD):
return
■ 気象予報データの取得
リクエストの送信先となるURLは、都市によって異なります。すべてのパターンを用意すると大変なので、以下のように共通のフォーマットをひとつ用意します。
"https://worldweather.wmo.int/en/json/[City ID]_en.json"
このフォーマットの[City ID]
の部分だけを、get_forecast()
関数で受け取った引数の番号で置き換えることでURLを作成します。
追加【6行目、13・14行目】
import urequests
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
url_format = "https://worldweather.wmo.int/en/json/[City ID]_en.json" # 全都市共通のURLフォーマット
wifi = MyWifi()
def get_forecast(city_id):
if not wifi.isconnected():
if not wifi.connect(SSID, PASSWORD):
return
url = url_format.replace("[City ID]", str(city_id)) # 都市番号は数値のため、文字列に変換することに注意
res = urequests.get(url)
■ ターミナルへの結果の表示
response
オブジェクトのjson()
メソッドでJSONデータを辞書型のオブジェクトに変換して、「都市名」「予報日(明日または今日)」「気象」「最高気温」「最低気温」の5つの情報を取り出します。そして、これら5つの情報を含むメッセージをターミナルに表示しましょう。
【 サンプルコード 4-2-1 】
追加【15行目~23行目】
import urequests
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
url_format = "https://worldweather.wmo.int/en/json/[City ID]_en.json"
wifi = MyWifi()
def get_forecast(city_id):
if not wifi.isconnected():
if not wifi.connect(SSID, PASSWORD):
return
url = url_format.replace("[City ID]", str(city_id))
res = urequests.get(url)
data = res.json()
name = data["city"]["cityName"] # 都市名
date = data["city"]["forecast"]["forecastDay"][0]["forecastDate"] # 予報日
weather = data["city"]["forecast"]["forecastDay"][0]["weather"] # 気象
max_temp = data["city"]["forecast"]["forecastDay"][0]["maxTemp"] # 最高気温
min_temp = data["city"]["forecast"]["forecastDay"][0]["minTemp"] # 最低気温
message = "city:{}\ndate:{}\nweather:{}\nmax_temp:{}\nmin_temp:{}" # ターミナルに表示するメッセージ
message = message.format(name, date, weather, max_temp, min_temp)
print(message) # メッセージの表示
■ プログラムの動作確認
完成したプログラムを実行する前に、気象予報データを取得したい都市を決めて(例えば自分の住んでいる都市もしくは一番近い都市など)、以下のページで固有番号を調べておきます。
https://worldweather.wmo.int/en/json/full_city_list.txt
プログラムを実行して、ターミナルでget_forecast()
関数に調べた番号を渡して呼び出しましょう。
(プログラムの実行例)
※ 「183」は日本の東京(Tokyo)を表しています。
>>> get_forecast(183) city:Tokyo date:2020-07-29 weather:Rain max_temp:27 min_temp:24
チャプター5
課題:気象を表すアイコンを作成してLEDディスプレイに表示する
【 サンプルコード 4-2-1 】では、取得した気象予報をターミナルに表示しました。この課題では、気象を表すアイコン(イメージ)を用意して、追加でLEDディスプレイに表示するようにプログラムを改造してみましょう。
5. 1 ヒント:気象を表すアイコンの定義
前のチャプターで気象を表す名前が数多くあることを紹介しました。すべての気象に対してアイコンを用意するのは大変ですので、気象を次の5種類に分類します。
分類 | 気象名 |
---|---|
晴れ | No Rain, Fine, Sunny, Clear, Clearing, Bright”, Fair, Mild |
雨 | Thunderstorms, Thundershowers, Storm, Showers, Heavy Showers, Rainshower, Occasional Showers, Scattered Showers, Isolated Showers, Light Showers, Freezing Rain, Rain, Drizzle, Light Rain, Squall |
曇り | Cloudy, Partly Cloudy, Sunny Intervals, Mostly Cloudy, Partly Bright |
雪 | Snow, Flurries, Heavy Snow, Snowfall, Light Snow, Blizzard, Blowing Snow, Hail, Snow Showers, Snowstorm, Snowdrift |
その他 | Sandstorm, Volcanic Ash など |
そして5分類に対して、それぞれ次のアイコンを設定しましょう。
5. 2 プログラムの作成手順
次の手順でプログラムを改造します。
■ 気象の分類の定義
始めに気象の分類を定義します。上の表で示した分類ごとに、気象名をまとめた集合を作成しましょう。その他の分類については、他の4分類以外と考えることができるので、集合を定義する必要はありません。
追加【10行目~20行目】
import urequests
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
url_format = "https://worldweather.wmo.int/en/json/[City ID]_en.json"
wifi = MyWifi()
# 気象の分類
WORDS_FINE = {"No Rain", "Fine", "Sunny", "Clear", "Clearing", "Bright",
"Fair", "Mild"} # 晴れ
WORDS_RAINY = {"Thunderstorms", "Thundershowers", "Storm", "Showers",
"Heavy Showers", "Rainshower", "Occasional Showers",
"Scattered Showers", "Isolated Showers", "Light Showers",
"Freezing Rain", "Rain", "Drizzle", "Light Rain", "Squall"} # 雨
WORDS_CLOUDY = {"Cloudy", "Partly Cloudy", "Sunny Intervals",
"Mostly Cloudy", "Partly Bright"} # 曇り
WORDS_SNOWY = {"Snow", "Flurries", "Heavy Snow", "Snowfall", "Light Snow",
"Blizzard", "Blowing Snow", "Hail", "Snow Showers",
"Snowstorm", "Snowdrift"} # 雪
■ 気象を表すアイコンの定義
次に分類した各気象を表すアイコンを定義します。
追加【2行目、23行目~27行目】
import urequests
from pystubit.board import Image, display # イメージ作成用クラスのインポート
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
url_format = "https://worldweather.wmo.int/en/json/[City ID]_en.json"
wifi = MyWifi()
WORDS_FINE = {"No Rain", "Fine", "Sunny", "Clear", "Clearing", "Bright",
"Fair", "Mild"}
WORDS_RAINY = {"Thunderstorms", "Thundershowers", "Storm", "Showers",
"Heavy Showers", "Rainshower", "Occasional Showers",
"Scattered Showers", "Isolated Showers", "Light Showers",
"Freezing Rain", "Rain", "Drizzle", "Light Rain", "Squall"}
WORDS_CLOUDY = {"Cloudy", "Partly Cloudy", "Sunny Intervals",
"Mostly Cloudy", "Partly Bright"}
WORDS_SNOWY = {"Snow", "Flurries", "Heavy Snow", "Snowfall", "Light Snow",
"Blizzard", "Blowing Snow", "Hail", "Snow Showers",
"Snowstorm", "Snowdrift"}
# 各気象のアイコン(イメージ)
IMG_FINE = Image("01110:11111:11111:11111:01110:", color=(30, 10, 0)) # 晴れ
IMG_RAINY = Image("00100:01110:11111:00100:01100:", color=(0, 10, 30)) # 雨
IMG_CLOUDY = Image("00000:01110:11111:01110:00000:", color=(15, 15, 15)) # 曇り
IMG_SNOWY = Image("10101:01110:11011:01110:10101:", color=(30, 30, 30)) # 雪
IMG_OTHERS = Image("00000:00000:01110:00000:00000:", color=(10, 5, 15)) # その他
■ 気象名からアイコンを返す関数の作成
引数に気象名を渡すと対応するアイコンのイメージを返す関数を用意します。
追加【29行目~40行目】
IMG_FINE = Image("01110:11111:11111:11111:01110:", color=(30, 10, 0))
IMG_RAINY = Image("00100:01110:11111:00100:01100:", color=(0, 10, 30))
IMG_CLOUDY = Image("00000:01110:11111:01110:00000:", color=(15, 15, 15))
IMG_SNOWY = Image("10101:01110:11011:01110:10101:", color=(30, 30, 30))
IMG_OTHERS = Image("00000:00000:01110:00000:00000:", color=(10, 5, 15))
def get_icon(weather):
if weather in WORDS_FINE: # in演算子で集合にその気象名が含まれているかを確認
img = IMG_FINE # 含まれている場合は対応するアイコンのイメージを返す
elif weather in WORDS_RAINY:
img = IMG_RAINY
elif weather in WORDS_CLOUDY:
img = IMG_CLOUDY
elif weather in WORDS_SNOWY:
img = IMG_SNOWY
else:
img = IMG_OTHERS # どれにも当てはまらない場合は「その他」の分類とみなす
return img
■ 気象を表すアイコンの表示
最後に、LEDディスプレイへ気象を表すアイコンを表示します。これでプログラムの完成です。プログラムを実行して動作を確認しましょう。
【 サンプルコード 5-2-1 】
追加【55行目、57行目】
import urequests
from pystubit.board import Image, display
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
url_format = "https://worldweather.wmo.int/en/json/[City ID]_en.json
"wifi = MyWifi()
WORDS_FINE = {"No Rain", "Fine", "Sunny", "Clear", "Clearing", "Bright",
"Fair", "Mild"}
WORDS_RAINY = {"Thunderstorms", "Thundershowers", "Storm", "Showers",
"Heavy Showers", "Rainshower", "Occasional Showers",
"Scattered Showers", "Isolated Showers", "Light Showers",
"Freezing Rain", "Rain", "Drizzle", "Light Rain", "Squall"}
WORDS_CLOUDY = {"Cloudy", "Partly Cloudy", "Sunny Intervals",
"Mostly Cloudy", "Partly Bright"}
WORDS_SNOWY = {"Snow", "Flurries", "Heavy Snow", "Snowfall", "Light Snow",
"Blizzard", "Blowing Snow", "Hail", "Snow Showers",
"Snowstorm", "Snowdrift"}
IMG_FINE = Image("01110:11111:11111:11111:01110:", color=(30, 10, 0))
IMG_RAINY = Image("00100:01110:11111:00100:01100:", color=(0, 10, 30))
IMG_CLOUDY = Image("00000:01110:11111:01110:00000:", color=(15, 15, 15))
IMG_SNOWY = Image("10101:01110:11011:01110:10101:", color=(30, 30, 30))
IMG_OTHERS = Image("00000:00000:01110:00000:00000:", color=(10, 5, 15))
def get_icon(weather):
if weather in WORDS_FINE:
img = IMG_FINE
elif weather in WORDS_RAINY:
img = IMG_RAINY
elif weather in WORDS_CLOUDY:
img = IMG_CLOUDY
elif weather in WORDS_SNOWY:
img = IMG_SNOWY
else:
img = IMG_OTHERS
return img
def get_forecast(city_id):
if not wifi.isconnected():
if not wifi.connect(SSID, PASSWORD):
return
url = url_format.replace("[City ID]", str(city_id))
res = urequests.get(url)
data = res.json()
name = data["city"]["cityName"]
date = data["city"]["forecast"]["forecastDay"][0]["forecastDate"]
weather = data["city"]["forecast"]["forecastDay"][0]["weather"]
max_temp = data["city"]["forecast"]["forecastDay"][0]["maxTemp"]
min_temp = data["city"]["forecast"]["forecastDay"][0]["minTemp"]
message = "city:{}\ndate:{}\nweather:{}\nmax_temp:{}\nmin_temp:{}"
message = message.format(name, date, weather, max_temp, min_temp)
img = get_icon(weather) # アイコンの取得
print(message)
display.show(img, delay=5000, clear=True) # アイコンを5秒間だけ表示
チャプター6
おわりに
6. 1 このレッスンのまとめ
このレッスンでは、HTTPのPOSTメソッドを実践し、またWebのアプリケーション間で受け渡しされるデータのフォーマットとして広く採用されている「JSON」について新たに学習しました。
そして、学習の応用として世界気象機関(WMO)のWebサイトから世界の各都市の気象予報データを取得するプログラムを作成しました。
今回利用した世界気象機関だけでなく、他にも様々な公的機関や民間企業のWebサイトでもJSONの形式で様々なデータが提供されています。興味のある人はぜひどのようなデータが取得できるのか検索して調べてみてください。
6. 2 次回のレッスンについて
次回のレッスンでは、ネットワークを利用してコンピュータに正確な時刻を設定する「NTP(Network Time Protocol)」という通信プロトコルについて学習します。