先講結論:健保署的院所 API 有一個不發出任何聲音的陷阱——你帶 limit=40000 去要全台灣的健保特約醫院診所,它回你 HTTP 200、整整 1,000 筆,沒有錯誤、沒有警告。2026-09-24 實測:result.limit 悄悄寫著 1000,result.total 誠實寫著 37,152。只看 records 長度的程式,會以為全台灣只有 1,000 家醫院,然後安心下班。地雷圖鑑裡最危險的一種:靜默截斷。
這不是假想敵。taiwan-hospital 收錄時的全量下載範例,就是單次把 limit 拉高抓全部——9/20 的硬化工程(0.8.1)做文件程式實測時才抓到:38 頁才能取齊的 37,133 筆,舊寫法永遠只拿到 1,000 筆。修正後的版本先看 result.total,再用 offset 一頁一頁取。文件寫的程式要能被 CI 直接執行,就是為了攔這種「看起來能跑」的程式。
import json, urllib.request
UA = {'User-Agent': 'Mozilla/5.0'}
BASE = 'https://info.nhi.gov.tw/api/iode0010/v1/rest/datastore/A21030000I-D2100G-001'
def page(offset):
req = urllib.request.Request(f'{BASE}?limit=1000&offset={offset}', headers=UA)
return json.loads(urllib.request.urlopen(req, timeout=40).read())['result']
first = page(0)
total, recs = first['total'], list(first['records'])
for off in range(1000, total, 1000):
recs += page(off)['records']
print(total, len(recs))
2026-09-24 實測輸出:37152 37152——38 頁,一页不缺。資料源:健保署開放資料 A21030000I-D2100G-001(醫事機構基本資料),免 key、免登入。
37,152 家的分佈長這樣(2026-09-24 實測,依 HOSP_ATTR_NAME):
| 類型 | 家數 |
|---|---|
| 私立西醫診所 | 11,970 |
| 其他(多為藥局) | 11,873 |
| 私立牙醫診所 | 7,711 |
| 私立中醫診所 | 4,636 |
| 衛生所 | 349 |
| 私立西醫醫院 | 235 |
「其他」那一萬多家,點開名字幾乎都是藥局——富康活力、康是美、安安藥局這些連鎖全在裡面。名稱含「診所」兩字的合計 24,354 家。隨手抽一個生活例子:台北市大同區 224 家,鈞生診所在甘州街 28 號,電話 (02)2557-0048。順帶一個眼熟的細節:地址欄是全形數字(鄭州路145號),Day 11 的教訓繼續適用。
文件上寫的是 37,133(2026-09-20 實測),今天早上實測已經是 37,152——四天多了 19 家。更妙的是,同一次實測裡前後兩次呼叫,total 從 37,149 變成 37,152:名錄後台幾乎是即時在更新的。所以回答這類問題一定要附查詢日期,這也是 Day 15 那堂課的續集——基準數字會漂,CI 要每晚重問。只是健保署這站有個小麻煩:它會斷 GitHub Actions 的 IP,CI 上的健保檢查多半只能 WARN 跳過,這種漂移在 CI 上反而是看不見的,得靠不佈署在雲上的眼睛。
result.total,對不上就重取或回報,不把殘缺當全部。第 17 天。地雷圖鑑給這條的位置很前面:會報錯的 API 不可怕,可怕的是 200 OK 配上悄悄縮水的資料。