Day 3 結尾說過,YouBike 統一 feed 隔天來了一個大反轉。結果反轉晚了一天才兌現:昨天早上打開 GitHub,health-check 自動開了 issue,CI 整排紅的。昨天才風光上線的統一 feed,出事了。而接下來這一天,我先是猜錯了兇手,然後花了半天越獄,最後決定不越獄。
案情本身很單純。apis.youbike.com.tw 那支統一 feed——一次回全台 14 個服務區、9,521 個站(2026-09-10 實測)的那支——GitHub Actions 去要,拿到的不是 JSON,是 503 加一個 Incapsula 的 HTML 挑戰頁。從台灣 IP 要,好好的。我第一個直覺是:地區封鎖,美國 IP 被擋。
今天(2026-09-12)我從雲端機房再驗一次:
curl -s -o page.html -w 'HTTP %{http_code}, %{size_download} bytes' "https://apis.youbike.com.tw/json/station-yb2.json"
HTTP 503, 885 bytes
885 bytes 裡沒有半輛 YouBike。但我越想越不對——「美國 IP 被擋」這個結論太好猜了,好猜到可疑。於是做了三個對照實驗:GCP 奧勒岡機房(道地美國資料中心 IP)→ 200 正常;兩個美國的免費代理 → 也是 200。換句話說,美國 IP 根本沒被擋,被擋的是 GitHub Actions 那個共用 IP 段。這不是地理問題,是名聲問題——GHA 的 IP 段被太多爬蟲用過,在 Incapsula 的黑名單上。兇手不是「你從哪國來」,是「你跟誰住同一棟樓」。
追查的時候還發現,health-check 連續失敗的主因其實不全是 Incapsula,是我自己的 check-urls.sh:文件裡像 ETW183W2_<期別> 這種帶佔位符的模板 URL,腳本在 < 的地方直接把字串切斷,然後拿 ETW183W2_ 這種殘段認真去 curl,當然 404。等於我的 CI 每天半夜對著一堆不存在的網址報警。模板 URL 現在整段擷取後標記 templated 直接跳過,世界清靜了。
既然只是 IP 名聲問題,那給 CI 掛一個台灣代理不就解了?我真的去试了。免費台灣代理找了 18 個:17 個直接連不上,唯一活著的那個確實穿過了 Incapsula——然後以 0.7KB/s 的速度爬行,9,521 站的 JSON 大概要下載到天亮。VPN Gate 那種志工接力網路,台灣節點是零。免費的路全滅。
那就看付費的。按量計費的住宅代理,DataImpulse 1 美元 1GB(CI 一天跑一次,一個月大概 0.15 美元),IPRoyal 7 美元 1GB,SOAX 試用 1.99 美元。價格其實不貴,貴的是別的東西:為了一個 CI 打勾,我要在倉庫裡養一組代理憑證、處理帳單、祈禱代理商的 IP 池名聲比 GHA 好。想清楚之後的結論是:不繞了。統一 feed 沒有死,只是 CI 看不到它;各市府 feed 都還活著當備援,台灣 IP 手動驗證也還在。這道牆不值得翻,繞過去的能力我證明過了,這就夠了。
所以 youbike.com.tw 網域在 CI 裡維持 skip-with-reason——跳過,但註明為什麼跳過。文件裡的說法也改了:不再是籠統的「地區封鎖」,而是誠實寫「Incapsula 會擋部分機房與海外 IP(2026-09-11 實測:GitHub Actions 失敗,其他海外 IP 正常)」。SKILL.md、功能文件、英文摘要同步補上:統一 feed 遇到 503 或非 JSON 時降級到市府 feed,手動驗證請從台灣 IP。
順手修了另一個遺憾:health-check 以前只會在失敗時開 issue,恢復正常後那張單就一直掛著,像病好了還貼著藥布。現在全綠的時候 CI 會自己到舊 issue 留言「已恢復」然後關閉。
今天 README 多了一個「評估後否決」區塊,專門記錄研究過但放棄的來源,避免三個月後的自己重踩。第一批兩筆:台銀牌告匯率——2026-09-11 實測,HTTP 200 但回的是 Challenge Validation 頁,bot 防護擋程式存取;TDX 的高鐵、台鐵、公車——2026-09-11 實測 token 端點回 invalid_client,要註冊拿 Client ID / Secret。這個倉庫的入場券是免金鑰、免登入、官方來源,不合的就寫下來,放在大家看得到的地方。
0.3.1 到 0.3.3 三個 patch:CI 模板 URL 修正、Incapsula 降級指引、health-check 自動關單、文件標點統一全形、README 否決清單。外加一個不寫進 changelog 的決定:不為打勾養代理。今天學到兩件事:「我這裡被擋」不等於「全世界都被擋」——封鎖的單位是 IP 名聲,不是國界;以及,證明自己能翻牆之後選擇不翻,也是一種工程判斷。