Python

[Python 爬蟲] 取得中央氣象局測站觀測資料的三種方法

(本文同步發表於計算思維學院)

有學員發問:該如何下載中央氣象局的測站觀測資料?雖然你點選網頁之後會看到過去 24 小時的資料表格,但是直接使用 requests.get() 取得的網頁文件並沒有包含該資料表。這是因為該網頁是使用 AJAX 的非同步更新方式,也就是瀏覽器會另外在背景呼叫 JavaScript 函式,向 Server 取得資料後,再即時更新網頁結構,因此原本的網頁原始碼並不會包含資料部份。

分析網頁隱藏的 API Endpoint (以氣象資料來說不推薦)

以中央氣象局的氣象資料來說,我們不推薦這個方式,因為已經有其他更簡便的管道可以取得資料:官方 API、政府開放資料、或是民間打包好的資料下載。以下說明是在沒有其它管道的情況下,必須自己分析隱藏的 API Endpoint 時使用)

怎麼知道該網頁是使用 AJAX 的更新方式?打開開發者工具,在 Network 部份檢視 Fetch/XHR 請求,重新整理網頁後,逐一檢視瀏覽器送出的請求,會看到一個我們特別有興趣的請求,因為其回傳就是資料表格本身!

檢視該請求的 Reqeust URL, 簡單測試後,就能看出取得某測站過去 24 小時的 Endpoint 是:

https://www.cwb.gov.tw/V8/C/W/Observe/MOD/24hr/[測站 ID].html

在上面例子中,台北測站的 ID 是 46692. 要怎麼知道某個測站的 ID? 通常我們會回到選擇測站的網頁,檢視下拉式選單或超連結,看看有沒有相關線索。我們的確也在該網頁原始碼中看到了測站 ID 的資料:

但是這個例子中還有更快的方法!在一開始的測站觀測資料網頁中,我們看到了另外一個 XHR 請求 (STMap.json),其回傳內容包含了全部的測站 ID

知道了 API endpoint 與其參數邏輯,就能夠輕易地與其互動,直接取得資料:

import requests
from bs4 import BeautifulSoup
resp = requests.get("https://www.cwb.gov.tw/V8/C/W/Observe/MOD/24hr/46692.html")
soup = BeautifulSoup(resp.text, "html.parser")
for row in soup.find_all("tr"):
    print(list(row.stripped_strings))
# Output:
# ['05/12', '22:20', '23.8', '75', '南南西', '0.6', '1', '-', '-', '16-20', '89', '1014.7', '0.5', '0.1']
# ['05/12', '22:10', '23.7', '75', '南南西', '0.7', '1', '-', '-', '16-20', '89', '1014.7', '0.5', '0.1']
# ['05/12', '22:00', '23.7', '75', '南南西', '0.9', '1', '2.1', '2', '11-15', '90', '1014.7', '0.5', '0.1']
# ...

使用官方網站 API (推薦)

分析網頁背後隱藏的 API Endpoint 是不得已的辦法。以氣象資料來說,中央氣象局已經提供了開放資料與 API 讓我們直接取得資料。我們可以直接在氣象資料開放平台註冊帳號並取得授權碼,接著直接進入有興趣的資料主題,以局屬氣象站-現在天氣觀測報告為例,可以直接下載資料,或是到 API 測試區測試,輸入授權碼及測站代碼後,就能看到 API Endpoint 的格式及回傳結果。

搜尋下載別人打包好的資料 (推薦)

氣象資料是需求量滿大的資料,因此我們可以搜尋有沒有任何官方或民間整理好的資料庫,可以直接打包下載。我們找到了兩個資料庫:

  • 氣象局的觀測資料查詢。可以選擇測站,與報表格式 (日報/月報/年報)
  • 民間整理的自動氣象站觀測資料彙整。這邊收錄了「氣象資料開放平台」所提供的自動氣象站觀測資料的歷史數據,根據測站及月份分類,例如臺北測站的最近三個月資料如下圖

以上就是取得中央氣象局測站觀測資料的三種方法。這個案例正好對應到我們在爬蟲課程一開頭就強調的原則:以終為始。程式只是工具,如果有更快的方式能夠取得資料,一行程式都不用寫,何樂而不為?

想系統化學習 Python 網路爬蟲,可以參考 Python 網頁爬蟲入門實戰:經典長銷、千人好評的 Python 爬蟲課程