奈良の天気予報のデータを表示してみよう。使いやすい予報データは以下のところから得られる。
ここではWeather Hacksのデータを使う。予報データは日本気象協会が作成しライブドアが配信している。
from urllib.request import urlopen
import json
city = 290010 # 奈良
url = "http://weather.livedoor.com/forecast/webservice/json/v1?city=" + str(city)
response = urlopen(url)
content = json.loads(response.read().decode("utf8"))
content
{'copyright': {'image': {'height': 26, 'link': 'http://weather.livedoor.com/', 'title': 'livedoor 天気情報', 'url': 'http://weather.livedoor.com/img/cmn/livedoor.gif', 'width': 118}, 'link': 'http://weather.livedoor.com/', 'provider': [{'link': 'http://tenki.jp/', 'name': '日本気象協会'}], 'title': '(C) LINE Corporation'}, 'description': {'publicTime': '2017-08-20T10:32:00+0900', 'text': ' 近畿地方は、高気圧に覆われて、晴れています。\n\n 今日の奈良県は、高気圧に覆われておおむね晴れますが、強い日射の影響\nで大気の状態が不安定となるため、昼過ぎから夜のはじめ頃にかけて雨や雷\n雨の所があるでしょう。\n 奈良県では、高温が予想され、熱中症の危険が特に高くなる見込みです。\n暑さを避け、水分をこまめに補給するなど、十分な対策をとってください。\n\n 明日の奈良県は、高気圧に覆われておおむね晴れますが、強い日射の影響\nで、大気の状態が不安定となるため、夕方からは雨や雷雨の所がある見込み\nです。'}, 'forecasts': [{'date': '2017-08-20', 'dateLabel': '今日', 'image': {'height': 31, 'title': '晴時々曇', 'url': 'http://weather.livedoor.com/img/icon/2.gif', 'width': 50}, 'telop': '晴時々曇', 'temperature': {'max': {'celsius': '35', 'fahrenheit': '95.0'}, 'min': None}}, {'date': '2017-08-21', 'dateLabel': '明日', 'image': {'height': 31, 'title': '晴時々曇', 'url': 'http://weather.livedoor.com/img/icon/2.gif', 'width': 50}, 'telop': '晴時々曇', 'temperature': {'max': {'celsius': '35', 'fahrenheit': '95.0'}, 'min': {'celsius': '24', 'fahrenheit': '75.2'}}}, {'date': '2017-08-22', 'dateLabel': '明後日', 'image': {'height': 31, 'title': '曇り', 'url': 'http://weather.livedoor.com/img/icon/8.gif', 'width': 50}, 'telop': '曇り', 'temperature': {'max': None, 'min': None}}], 'link': 'http://weather.livedoor.com/area/forecast/290010', 'location': {'area': '近畿', 'city': '奈良', 'prefecture': '奈良県'}, 'pinpointLocations': [{'link': 'http://weather.livedoor.com/area/forecast/2920100', 'name': '奈良市'}, {'link': 'http://weather.livedoor.com/area/forecast/2920200', 'name': '大和高田市'}, {'link': 'http://weather.livedoor.com/area/forecast/2920300', 'name': '大和郡山市'}, {'link': 'http://weather.livedoor.com/area/forecast/2920400', 'name': '天理市'}, {'link': 'http://weather.livedoor.com/area/forecast/2920500', 'name': '橿原市'}, {'link': 'http://weather.livedoor.com/area/forecast/2920600', 'name': '桜井市'}, {'link': 'http://weather.livedoor.com/area/forecast/2920701', 'name': '五條市北部'}, {'link': 'http://weather.livedoor.com/area/forecast/2920800', 'name': '御所市'}, {'link': 'http://weather.livedoor.com/area/forecast/2920900', 'name': '生駒市'}, {'link': 'http://weather.livedoor.com/area/forecast/2921000', 'name': '香芝市'}, {'link': 'http://weather.livedoor.com/area/forecast/2921100', 'name': '葛城市'}, {'link': 'http://weather.livedoor.com/area/forecast/2921200', 'name': '宇陀市'}, {'link': 'http://weather.livedoor.com/area/forecast/2932200', 'name': '山添村'}, {'link': 'http://weather.livedoor.com/area/forecast/2934200', 'name': '平群町'}, {'link': 'http://weather.livedoor.com/area/forecast/2934300', 'name': '三郷町'}, {'link': 'http://weather.livedoor.com/area/forecast/2934400', 'name': '斑鳩町'}, {'link': 'http://weather.livedoor.com/area/forecast/2934500', 'name': '安堵町'}, {'link': 'http://weather.livedoor.com/area/forecast/2936100', 'name': '川西町'}, {'link': 'http://weather.livedoor.com/area/forecast/2936200', 'name': '三宅町'}, {'link': 'http://weather.livedoor.com/area/forecast/2936300', 'name': '田原本町'}, {'link': 'http://weather.livedoor.com/area/forecast/2940100', 'name': '高取町'}, {'link': 'http://weather.livedoor.com/area/forecast/2940200', 'name': '明日香村'}, {'link': 'http://weather.livedoor.com/area/forecast/2942400', 'name': '上牧町'}, {'link': 'http://weather.livedoor.com/area/forecast/2942500', 'name': '王寺町'}, {'link': 'http://weather.livedoor.com/area/forecast/2942600', 'name': '広陵町'}, {'link': 'http://weather.livedoor.com/area/forecast/2942700', 'name': '河合町'}, {'link': 'http://weather.livedoor.com/area/forecast/2944100', 'name': '吉野町'}, {'link': 'http://weather.livedoor.com/area/forecast/2944200', 'name': '大淀町'}, {'link': 'http://weather.livedoor.com/area/forecast/2944300', 'name': '下市町'}], 'publicTime': '2017-08-20T11:00:00+0900', 'title': '奈良県 奈良 の天気'}
print(content['title'])
奈良県 奈良 の天気
print(content['description']['text'])
近畿地方は、高気圧に覆われて、晴れています。 今日の奈良県は、高気圧に覆われておおむね晴れますが、強い日射の影響 で大気の状態が不安定となるため、昼過ぎから夜のはじめ頃にかけて雨や雷 雨の所があるでしょう。 奈良県では、高温が予想され、熱中症の危険が特に高くなる見込みです。 暑さを避け、水分をこまめに補給するなど、十分な対策をとってください。 明日の奈良県は、高気圧に覆われておおむね晴れますが、強い日射の影響 で、大気の状態が不安定となるため、夕方からは雨や雷雨の所がある見込み です。
予報は今日,明日など複数あるので,for
文で反復する。
for forecast in content['forecasts']:
print(forecast['dateLabel'])
今日 明日 明後日
for forecast in content['forecasts']:
print(forecast['dateLabel'] + forecast['date'] + forecast['telop'])
今日2017-08-20晴時々曇 明日2017-08-21晴時々曇 明後日2017-08-22曇り
for forecast in content['forecasts']:
print(forecast['dateLabel'] + forecast['date'] + forecast['telop'])
print(forecast['temperature']['min'])
print(forecast['temperature']['max'])
今日2017-08-20晴時々曇 None {'celsius': '35', 'fahrenheit': '95.0'} 明日2017-08-21晴時々曇 {'celsius': '24', 'fahrenheit': '75.2'} {'celsius': '35', 'fahrenheit': '95.0'} 明後日2017-08-22曇り None None
気温データは摂氏(celsius)と華氏(fahrenheit)がある。
def c2f(c):
return 9/5 * c + 32
c2f(33)
91.4
摂氏だけ表示したいが…
for forecast in content['forecasts']:
print(forecast['dateLabel'] + forecast['date'] + forecast['telop'])
print(forecast['temperature']['min']['celsius'])
print(forecast['temperature']['max']['celsius'])
今日2017-08-20晴時々曇
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-9-d35fb1e416b7> in <module>() 1 for forecast in content['forecasts']: 2 print(forecast['dateLabel'] + forecast['date'] + forecast['telop']) ----> 3 print(forecast['temperature']['min']['celsius']) 4 print(forecast['temperature']['max']['celsius']) TypeError: 'NoneType' object is not subscriptable
今日の最低気温がNone
なのでエラーが出でしまった。None
であるときとないときをif
文で分けることにする。
for forecast in content['forecasts']:
print(forecast['dateLabel'] + forecast['date'] + forecast['telop'])
if forecast['temperature']['min'] != None:
print(forecast['temperature']['min']['celsius'])
else:
print("--")
if forecast['temperature']['max'] != None:
print(forecast['temperature']['max']['celsius'])
else:
print("--" )
今日2017-08-20晴時々曇 -- 35 明日2017-08-21晴時々曇 24 35 明後日2017-08-22曇り -- --
from urllib.request import urlopen
import json
def weather_forecast(city):
url = "http://weather.livedoor.com/forecast/webservice/json/v1?city=" + str(city)
response = urlopen(url)
content = json.loads(response.read().decode("utf8"))
print(content['title'])
print(content['description']['text'])
print()
for forecast in content['forecasts']:
dateLabel = forecast['dateLabel']
date = forecast['date']
telop = forecast['telop']
if forecast['temperature']['min'] != None:
tmin = forecast['temperature']['min']['celsius']
else:
tmin = "--"
if forecast['temperature']['max'] != None:
tmax = forecast['temperature']['max']['celsius']
else:
tmax = "--"
print(f"{dateLabel: <3}({date}){telop: <7}{tmin:>3}{tmax:>3}")
city = 290010 # 奈良
weather_forecast(city)
奈良県 奈良 の天気 近畿地方は、高気圧に覆われて、晴れています。 今日の奈良県は、高気圧に覆われておおむね晴れますが、強い日射の影響 で大気の状態が不安定となるため、昼過ぎから夜のはじめ頃にかけて雨や雷 雨の所があるでしょう。 奈良県では、高温が予想され、熱中症の危険が特に高くなる見込みです。 暑さを避け、水分をこまめに補給するなど、十分な対策をとってください。 明日の奈良県は、高気圧に覆われておおむね晴れますが、強い日射の影響 で、大気の状態が不安定となるため、夕方からは雨や雷雨の所がある見込み です。 今日 (2017-08-20)晴時々曇 -- 35 明日 (2017-08-21)晴時々曇 24 35 明後日(2017-08-22)曇り -- --
それぞれの日の予報と最低最高気温を印字する際,文字を揃えるためにフォーマット済み文字列リテラル(f文字列)を使っている。
f文字列はPythonバージョン3.6から使えるようになった新機能。
文字列のフォーマットにはいろいろあるが,直感的で分かりやすい。
{}
で囲んだ変数名の後の:
以降にフォーマットを記す。
次の例で最初の文字は,文字列の値が指定した桁数よりも短いときに埋めるために使う文字。
既定は半角の空白なので,上の関数では全角の空白を指定した。
<
は左揃え,^
は中揃え,>
は右揃え,最後の数字は桁数。
cities = ["Kobe", "Kyoto", "Nara", "Osaka", "Wakayama"]
for city in cities:
print(f"{city:_>8}")
____Kobe ___Kyoto ____Nara ___Osaka Wakayama
from urllib.request import urlopen
url = "http://weather.livedoor.com/forecast/rss/primary_area.xml"
response = urlopen(url)
data = response.read()
XMLはツリー構造をしているので,xmlモジュールを使ってルートを取得する。
import xml.etree.ElementTree as et
root = et.fromstring(data)
root[0]
<Element 'channel' at 0x000002942B533F48>
root
はリストになっていて,最初の要素がデータを含むchannel
である。その中から地点情報を含むノードを検索(find
)する。各県の下に地点のデータがある。ここでは地点名(title
)と地点番号(id
)を辞書に格納する。
loc = {}
for pref in root[0].find('{http://weather.livedoor.com/%5C/ns/rss/2.0}source'):
for city in pref.findall("city"):
loc[city.attrib['title']] = int(city.attrib['id'])
loc['京都']
260010
以上をモジュールにまとめる。weather.py
として保存する。
from urllib.request import urlopen
import xml.etree.ElementTree as et
import json
def get_loc():
url = "http://weather.livedoor.com/forecast/rss/primary_area.xml"
response = urlopen(url)
data = response.read()
root = et.fromstring(data)
loc = {}
for pref in root[0].find('{http://weather.livedoor.com/%5C/ns/rss/2.0}source'):
for city in pref.findall("city"):
loc[city.attrib['title']] = int(city.attrib['id'])
return loc
loc = get_loc()
def forecast(city):
url = "http://weather.livedoor.com/forecast/webservice/json/v1?city=" + str(city)
response = urlopen(url)
content = json.loads(response.read().decode("utf8"))
print(content['title'])
print(content['description']['text'])
print()
for forecast in content['forecasts']:
dateLabel = forecast['dateLabel']
date = forecast['date']
telop = forecast['telop']
if forecast['temperature']['min'] != None:
tmin = forecast['temperature']['min']['celsius']
else:
tmin = "--"
if forecast['temperature']['max'] != None:
tmax = forecast['temperature']['max']['celsius']
else:
tmax = "--"
print(f"{dateLabel: <3}({date}){telop: <7}{tmin:>3}{tmax:>3}")
import weather
loc = weather.loc
weather.forecast(loc['京都'])
京都府 京都 の天気 近畿地方は、高気圧に覆われて、晴れています。 今日の京都府は、高気圧に覆われておおむね晴れますが、強い日射の影響 で大気の状態が不安定となるため、雷を伴い激しい雨の降る所がある見込み です。 京都府では高温が予想され、熱中症の危険が特に高くなる見込みです。暑 さを避け、水分をこまめに補給するなど、十分な対策をとってください。 明日の京都府は、高気圧に覆われておおむね晴れますが、午後は強い日射 の影響で大気の状態が不安定となるため、雨や雷雨の所があるでしょう。 今日 (2017-08-20)晴時々曇 -- 35 明日 (2017-08-21)晴時々曇 25 36 明後日(2017-08-22)曇り -- --