在上一篇測試中, 擷取的對象是台北市公開資料平台提供的 Youbike 即時資料, 此平台直接提供 JSON 資料網址, 因此毋須註冊, 可直接取得或下載 JSON 資料. 另外一種提供公開 API 的網站則需要註冊帳號, 取得 API Key 才能獲准取得資料, 此類網站主要是為了控管使用者的呼叫頻率.
本系列之前的筆記參考 :
我在讀陳會安老師的 "Python 從初學到生活應用超實務" 這本書的 11-6 節時看到 OpenWeather 的介紹, OpenWeather 是位於英國倫敦的 OpenWeather 氣象網站, 該組織自 2014 年起向全球提供世界上超過 20 萬個城市的氣象資訊, 使用者可註冊帳號免費取得 API Key 取得天氣資料. 參考下列教學文件 :
以下是實測紀錄.
一. 註冊 OpenWeather 帳號 :
請按首頁右上角的 "Sign In" :
於登入頁面中按右下角的 "Create an Account" :
於註冊頁面輸入帳號, e-mail, 密碼等並勾選年齡與隱私權欄位後按 "Create an Account" 鈕 :
建立帳號後會收到一封驗證信, 開啟後按信中的驗證按鈕即完成註冊 :
登入帳戶後會先調查使用 OpenWeather 的用途 :
然後會跳轉到付費方案的價格表頁面 :
OpenWeather 也有依用量計費的 "Pay as You Go" 付費方案, 每天前 1000 個 API 請求免費, 超過的部分每次請求以 0.0012 英鎊計費 :
另外 OpenWeather 也提供免費方案, 免費帳戶每分鐘可以請求 60 次 (平均每分鐘一次), 但一個月總呼叫量上限為 100 萬次 :
參考 :
如果呼叫數超過免費額度會收到如下回應 :
{ "cod": 429,
"message": "Your account is temporary blocked due to exceeding of requests limitation of your subscription type.
Please choose the proper subscription http://openweathermap.org/price"
}
# https://openweathermap.org/appid
取得 API Key (金鑰) 後便可從 OpenWeather 網站取得即時氣象資訊, API 的網址格式如下 :
二. 取得 OpenWeather API Key (金鑰) :
接下來切到 "API Key" 頁籤, 預設已經為我們產生一個名為 "Default" 的金鑰, 也可以在右方輸入框自訂 Key 名稱, 然後按 Generate 鈕產生新的金鑰, 其數量並無限制 :
將 API Key 複製到記事本中保存備用.
二. 取得即時氣象資料 :
https://api.openweathermap.org/data/2.5/weather?q={city},{country}&units=metric&lang=zh_tw&appid={api_key}
其中 country 參數為國家名稱, 採用 ISO-3166 兩位字母代碼, 台灣的國家代碼是 'TW'. api_key 參數為上面所取得之金鑰, 而 city 參數為城市名稱, 對台灣而言, 它提供了如下 6 個城市代號 :
- 'Taipei' : 台北
- 'Banqiao' : 板橋
- 'Taoyuan' : 桃園
- 'Hsinchu' : 新竹
- 'Taichung' : 台中
- 'Tainan' : 台南
- 'Kaohsiung' : 高雄
這樣就可以開始測試了, 首先匯入 requests 與 json 套件 :
>>> import requests
>>> import json
然後設定參數 :
>>> api_key='c1ca9d88d1ctony19664b3c021cedf7e' # 範例金鑰
>>> city='Kaohsiung'
>>> country='TW'
用 f 字串製作請求之 URL :
>>> url=f'https://api.openweathermap.org/data/2.5/weather?q={city},{country}&units=metric&lang=zh_tw&appid={api_key}'
>>> url
'https://api.openweathermap.org/data/2.5/weather?q=Kaohsiung,TW&units=metric&lang=zh_tw&appid=c1ca9d88d1ctony19664b3c021cedf7e'
呼叫 requests.get() 函式提出 HTTP GET 請求來取得氣象資訊並使用 json.loads() 將傳回的 JSON 字串轉成 Python 字典 :
>>> res=requests.get(url)
>>> data=json.loads(res.text)
>>> data
{'coord': {'lon': 120.3133, 'lat': 22.6163}, 'weather': [{'id': 701, 'main': 'Mist', 'description': '薄霧', 'icon': '50n'}], 'base': 'stations', 'main': {'temp': 26.81, 'feels_like': 29.93, 'temp_min': 24.05, 'temp_max': 26.97, 'pressure': 1012, 'humidity': 87}, 'visibility': 5000, 'wind': {'speed': 3.09, 'deg': 20}, 'clouds': {'all': 40}, 'dt': 1715022884, 'sys': {'type': 2, 'id': 2002588, 'country': 'TW', 'sunrise': 1715030550, 'sunset': 1715077684}, 'timezone': 28800, 'id': 1673820, 'name': 'Kaohsiung City', 'cod': 200}
注意, 最後一個鍵 cod 為 HTTP 回應狀態碼, 200 表示 OK.
關於 json 套件用法參考 :
上面的字典輸出內容擠成一團不好閱讀, 可以用 pprint 模組的 pprint() 函式來輸出 :
>>> from pprint import pprint
>>> pprint(data)
{'base': 'stations',
'clouds': {'all': 40},
'cod': 200,
'coord': {'lat': 22.6163, 'lon': 120.3133},
'dt': 1715022884,
'id': 1673820,
'main': {'feels_like': 29.93,
'humidity': 87,
'pressure': 1012,
'temp': 26.81,
'temp_max': 26.97,
'temp_min': 24.05},
'name': 'Kaohsiung City',
'sys': {'country': 'TW',
'id': 2002588,
'sunrise': 1715030550,
'sunset': 1715077684,
'type': 2},
'timezone': 28800,
'visibility': 5000,
'weather': [{'description': '薄霧', 'icon': '50n', 'id': 701, 'main': 'Mist'}],
'wind': {'deg': 20, 'speed': 3.09}}
關於 pprint 模組用法參考 :
我們主要關心的氣象資訊為 weather 鍵中的 discription (天氣描述) 與 main 鍵的溫度, 氣壓, 與濕度資訊, weather 鍵的值是一個單一元素的串列, 可用 data['weather'][0] 取得此串列, 再用 description 鍵取得天氣描述. main 鍵的值是一個字典, 可用 data['main']['temp'] 取得目前溫度 :
>>> data['weather'][0]['description']
'薄霧'
>>> data['main']['temp']
26.81
>>> data['main']['temp_min']
24.05
>>> data['main']['temp_max']
26.97
>>> data['main']['pressure']
1012
>>> data['main']['humidity']
87
另外回應資料中的 id 鍵 (此處為 1673820) 代表查詢城市 (此處為 Kaohsiung) 的地理位置代號 ( geocode), 這個在查詢未來五天氣象預報時會用到 :
三. 取得未來 5 天氣象預報資料 :
透過上面查詢即時氣象資料得到的 geocode 搭配 API Key 可用來查詢該地的未來一周氣象預報, 其 API 網址格式如下 :
http://api.openweathermap.org/data/2.5/forecast?id={geocode}&appid={api_key}
參考 :
例如 :
>>> import requests
>>> import json
>>> from pprint import pprint
>>> api_key='c1ca9d88d1ctony19664b3c021cedf7e' # 範例金鑰
>>> url=f'http://api.openweathermap.org/data/2.5/forecast?id=1673820&appid={api_key}'
>>> res=requests.get(url)
>>> data=json.loads(res.text)
>>> pprint(data)
{'city': {'coord': {'lat': 22.6163, 'lon': 120.3133},
'country': 'TW',
'id': 1673820,
'name': 'Kaohsiung City',
'population': 0,
'sunrise': 1715030550,
'sunset': 1715077684,
'timezone': 28800},
'cnt': 40,
'cod': '200',
'list': [{'clouds': {'all': 60},
'dt': 1715072400,
'dt_txt': '2024-05-07 09:00:00',
'main': {'feels_like': 306.74,
'grnd_level': 1010,
'humidity': 60,
'pressure': 1011,
'sea_level': 1011,
'temp': 303.54,
'temp_kf': 1.61,
'temp_max': 303.54,
'temp_min': 301.93},
'pop': 0.24,
'sys': {'pod': 'd'},
'visibility': 10000,
'weather': [{'description': 'broken clouds',
'icon': '04d',
'id': 803,
'main': 'Clouds'}],
'wind': {'deg': 299, 'gust': 3.67, 'speed': 3.94}},
{'clouds': {'all': 80},
'dt': 1715083200,
'dt_txt': '2024-05-07 12:00:00',
'main': {'feels_like': 305.54,
'grnd_level': 1012,
'humidity': 65,
'pressure': 1012,
'sea_level': 1012,
'temp': 302.43,
'temp_kf': 0.95,
'temp_max': 302.43,
'temp_min': 301.48},
'pop': 0.24,
'sys': {'pod': 'n'},
'visibility': 10000,
'weather': [{'description': 'broken clouds',
'icon': '04n',
'id': 803,
'main': 'Clouds'}],
'wind': {'deg': 270, 'gust': 2.21, 'speed': 1.55}},
{'clouds': {'all': 100},
'dt': 1715094000,
'dt_txt': '2024-05-07 15:00:00',
'main': {'feels_like': 303.46,
'grnd_level': 1012,
'humidity': 71,
'pressure': 1013,
'sea_level': 1013,
'temp': 300.9,
'temp_kf': 0,
'temp_max': 300.9,
'temp_min': 300.9},
'pop': 0,
'sys': {'pod': 'n'},
'visibility': 10000,
'weather': [{'description': 'overcast clouds',
'icon': '04n',
'id': 804,
'main': 'Clouds'}],
'wind': {'deg': 220, 'gust': 2.4, 'speed': 2.16}},
... (略) ...
{'clouds': {'all': 28},
'dt': 1715482800,
'dt_txt': '2024-05-12 03:00:00',
'main': {'feels_like': 304.34,
'grnd_level': 1011,
'humidity': 60,
'pressure': 1012,
'sea_level': 1012,
'temp': 302.24,
'temp_kf': 0,
'temp_max': 302.24,
'temp_min': 302.24},
'pop': 0,
'sys': {'pod': 'd'},
'visibility': 10000,
'weather': [{'description': 'scattered clouds',
'icon': '03d',
'id': 802,
'main': 'Clouds'}],
'wind': {'deg': 279, 'gust': 2.77, 'speed': 3.11}},
{'clouds': {'all': 53},
'dt': 1715493600,
'dt_txt': '2024-05-12 06:00:00',
'main': {'feels_like': 305.66,
'grnd_level': 1008,
'humidity': 59,
'pressure': 1009,
'sea_level': 1009,
'temp': 303.08,
'temp_kf': 0,
'temp_max': 303.08,
'temp_min': 303.08},
'pop': 0.01,
'sys': {'pod': 'd'},
'visibility': 10000,
'weather': [{'description': 'broken clouds',
'icon': '04d',
'id': 803,
'main': 'Clouds'}],
'wind': {'deg': 295, 'gust': 4.74, 'speed': 5.17}}],
'message': 0}
可見此 API 會回應 5/7~5/12 共 5 天的氣象預報資料 (每 3 個小時更新一次), 注意, cod 鍵為 HTTP 回應狀態碼, '200' 表示 OK, 注意, 與上面即時氣象回應值為數字不同的是, 此處是字串 '200'.
此處因輸出資料甚長而略去中間輸出, 完整之輸出參考 :
從上面的輸出可知傳回值是一個 JSON 資料, 透過 json 套件轉成字典後可以很方便地取得內容, 例如 city 鍵儲存所查詢之城市的相關資訊, 例如 geocode(id), 經緯度, 與日昇日落時間等 :
>>> data['city']
{'coord': {'lat': 22.6163, 'lon': 120.3133}, 'country': 'TW', 'id': 1673820, 'name': 'Kaohsiung City', 'population': 0, 'sunrise': 1715030550, 'sunset': 1715077684, 'timezone': 28800}
預報資料放在 list 鍵裡, 其值是一個字典串列, 串列元素有 40 個, 每一個間隔 3 小時, 一天有 8 筆資料, 五天就是 40 筆 :
>>> len(data['list'])
40
檢視第一筆與最後一筆資料 :
>>> data['list'][0]
{'clouds': {'all': 60}, 'dt': 1715072400, 'dt_txt': '2024-05-07 09:00:00', 'main': {'feels_like': 306.74, 'grnd_level': 1010, 'humidity': 60, 'pressure': 1011, 'sea_level': 1011, 'temp': 303.54, 'temp_kf': 1.61, 'temp_max': 303.54, 'temp_min': 301.93}, 'pop': 0.24, 'sys': {'pod': 'd'}, 'visibility': 10000, 'weather': [{'description': 'broken clouds', 'icon': '04d', 'id': 803, 'main': 'Clouds'}], 'wind': {'deg': 299, 'gust': 3.67, 'speed': 3.94}}
>>> data['list'][39]
{'clouds': {'all': 53}, 'dt': 1715493600, 'dt_txt': '2024-05-12 06:00:00', 'main': {'feels_like': 305.66, 'grnd_level': 1008, 'humidity': 59, 'pressure': 1009, 'sea_level': 1009, 'temp': 303.08, 'temp_kf': 0, 'temp_max': 303.08, 'temp_min': 303.08}, 'pop': 0.01, 'sys': {'pod': 'd'}, 'visibility': 10000, 'weather': [{'description': 'broken clouds', 'icon': '04d', 'id': 803, 'main': 'Clouds'}], 'wind': {'deg': 295, 'gust': 4.74, 'speed': 5.17}}
我們關心的氣象資料分別在 dt_txt, main, 與 weather 等鍵底下, 存取方式以第一筆為例 :
>>> data['list'][0]['dt_txt']
'2024-05-07 09:00:00'
>>> data['list'][0]['main']['humidity']
60
>>> data['list'][0]['main']['pressure']
1011
>>> data['list'][0]['main']['temp']
303.54
>>> data['list'][0]['weather'][0]['description']
'broken clouds'
可以迭代 list 鍵的 40 個元素取出每 3 小時的預測氣象數據 :
>>> for i in data['list']:
print(f"時間 : {i['dt_txt']}")
print(f"溫度 : {i['main']['temp']}")
print(f"濕度 : {i['main']['humidity']}")
print(f"壓力 : {i['main']['pressure']}")
print(f"描述 : {i['weather'][0]['description']}")
print('-------------------------')
結果節錄如下 :
時間 : 2024-05-07 09:00:00
溫度 : 303.54
濕度 : 60
壓力 : 1011
描述 : broken clouds
-------------------------
時間 : 2024-05-07 12:00:00
溫度 : 302.43
濕度 : 65
壓力 : 1012
描述 : broken clouds
-------------------------
時間 : 2024-05-07 15:00:00
溫度 : 300.9
濕度 : 71
壓力 : 1013
描述 : overcast clouds
... (略) ...
時間 : 2024-05-12 00:00:00
溫度 : 300.68
濕度 : 65
壓力 : 1012
描述 : broken clouds
-------------------------
時間 : 2024-05-12 03:00:00
溫度 : 302.24
濕度 : 60
壓力 : 1012
描述 : scattered clouds
-------------------------
時間 : 2024-05-12 06:00:00
溫度 : 303.08
濕度 : 59
壓力 : 1009
描述 : broken clouds
-------------------------
注意, 氣象預報傳回的溫度為卡氏溫度 (絕對溫度, 單位 K), 需減掉 273.15 才是攝氏溫度.
四. 氣象爬蟲函式 :
將上面的程式碼改寫為函式如下 :
import requests
import json
def get_weather_now(country, city, api_key):
url=(f'https://api.openweathermap.org/data/2.5/weather?'
f'q={city},{country}&units=metric&lang=zh_tw&appid={api_key}')
try:
res=requests.get(url)
data=json.loads(res.text)
if data['cod']==200: # 注意是數值
ret={'geocode': data['id'],
'description': data['weather'][0]['description'],
'temperature': data['main']['temp'],
'min_temperature': data['main']['temp_min'],
'max_temperature': data['main']['temp_max'],
'presure': data['main']['pressure'],
'humidity': data['main']['humidity']}
return ret
else:
return None
except Exceptions as e:
return None
def get_weather_forecast(geocode, api_key):
url=(f'http://api.openweathermap.org/data/2.5/forecast?'
f'id={geocode}&appid={api_key}')
try:
res=requests.get(url)
data=json.loads(res.text)
ret=[]
if data['cod']=='200': # 注意是字串
for i in data['list']:
d={'time': i['dt_txt'],
'temperature': round(i['main']['temp']-273.15), # 卡式溫度轉攝氏
'humidity': i['main']['humidity'],
'pressure': i['main']['pressure'],
'description': i['weather'][0]['description']}
ret.append(d)
return ret
else:
return None
except Exceptions as e:
return None
if __name__ == '__main__':
country='TW'
city='Kaohsiung'
api_key='c1ca9d88d1ctony19664b3c021cedf7e' # 範例金鑰
ret=get_weather_now(country, city, api_key)
print(f'目前氣象: {ret}')
geocode=ret['geocode']
print(f'未來五天氣象:')
ret=get_weather_forecast(geocode, api_key)
for i in ret:
print(f"時間 : {i['time']}")
print(f"溫度 : {i['temperature']}")
print(f"濕度 : {i['humidity']}")
print(f"壓力 : {i['pressure']}")
print(f"描述 : {i['description']}")
print('-------------------------')
此處已經將五天氣象預測中的卡氏溫度轉成攝氏, 結果如下 :
>>> %Run get_open_weather.py
目前氣象: {'geocode': 1673820, 'description': '多雲', 'temperature': 27.12, 'min_temperature': 25.05, 'max_temperature': 27.24, 'presure': 1013, 'humidity': 80}
未來五天氣象:
時間 : 2024-05-08 15:00:00
溫度 : 27
濕度 : 81
壓力 : 1013
描述 : light rain
-------------------------
時間 : 2024-05-08 18:00:00
溫度 : 27
濕度 : 77
壓力 : 1013
描述 : light rain
-------------------------
時間 : 2024-05-08 21:00:00
溫度 : 26
濕度 : 72
壓力 : 1013
描述 : light rain
-------------------------
... (略) ...
-------------------------
時間 : 2024-05-13 09:00:00
溫度 : 28
濕度 : 64
壓力 : 1009
描述 : light rain
-------------------------
時間 : 2024-05-13 12:00:00
溫度 : 27
濕度 : 63
壓力 : 1012
描述 : light rain
-------------------------
因我目前尚無具體應用會用到氣象資料, 故先測到這樣就好了, 有需要再於此基礎上進一步開發.
沒有留言:
張貼留言