代理輪換只是一根拐杖,而非解決方案。大多數爬取作業之所以失敗,是因為它們將 IP 位址視為反機器人系統唯一會測量的訊號。現實是,現代的機器人管理系統——Akamai Bot Manager、Cloudflare Turnstile、Datadome——所擷取的指紋遠比來源 IP 多得多。輪換免費公共代理池對抗這些系統幾乎毫無用處,而且往往讓情況更糟。
IP 輪換的假象
當你每次請求都輪換 IP 時,等於在宣告自己是爬蟲。人類的瀏覽模式呈現黏性連線——單一 IP 持續數分鐘或數小時、一致的瀏覽器指紋、以及可預測的請求間隔。像 requests 搭配 Session 物件與輪換代理清單這類工具,會破壞所有這些訊號。Akamai 的 X-Akamai-Device-Fingerprint 標頭與 Cloudflare 的 cf-request-id 關聯,可以在 TLS 參數、HTTP/2 設定與時間點完全相同的情況下,將來自不同 IP 的請求連結起來。Datadome 的 JavaScript 挑戰則會檢查無頭瀏覽器的痕跡,這些痕跡不會因代理變更而消失。只輪換 IP 卻不輪換完整的客戶端指紋,就像換了車牌卻開同一輛車——收費攝影機仍然會標記你。
對於低速率、低流量的爬取,且目標網站僅使用基本的 IP 速率限制(例如每分鐘 10 次請求,無 JavaScript 挑戰),單一住宅 IP 通常就足夠了。多年來,我使用一個靜態 IP 加上禮貌的 time.sleep(2),成功爬取政府資料入口網站與公開 API。完全不需要代理。規則很簡單:如果該網站在 50 次請求後沒有出現挑戰頁面或 CAPTCHA,你就不需要輪換。
超越 IP 位址:指紋識別
反機器人系統現在每次請求會收集數十種訊號。User-Agent 字串很容易偽造,但 Accept-Language、Sec-CH-UA、Connection 與 Accept-Encoding 的順序則不然。更關鍵的是,TLS 指紋識別——以 JA3 雜湊標準化(參見 JA3)——會根據加密套件順序與 TLS 擴展清單來識別客戶端函式庫。Python 的 requests 函式庫(透過 urllib3)產生的 JA3 雜湊與 Chrome 124 截然不同。Cloudflare 的 Turnstile 與 Datadome 都會檢查 JA3。在保持相同 TLS 堆疊的情況下輪換 IP,只會讓每個請求看起來像是同一個自動化客戶端在不同出口節點之間跳躍。免費代理更會加劇這個問題,因為它們通常執行老舊的 OpenSSL 版本,或使用已被列入黑名單的機器人風格 TLS 配置。
HTTP/2 指紋識別更進一步。SETTINGS 幀、視窗更新值與串流並行參數會形成獨特的「HTTP/2 指紋」,Akamai 的 Bot Manager 會跨連線追蹤這些參數。一個沒有同時輪換 HTTP/2 實作的輪換代理池,很容易被聚類分析。要避開這些檢查,唯一的方法是使用真實的瀏覽器引擎(Puppeteer、Playwright)或精心打造的 TLS/HTTP 堆疊來模仿特定瀏覽器版本——即便如此,你還需要在同一個連線的多次請求中維持相同的指紋。
免費公共代理池的經濟學
根據我的測試,免費公共代理清單的失敗率高達 60% 到 80%。大多數代理不是一上線就失效、被主機限制流量,就是已被主要機器人管理系統標記。從公開目錄擷取的免費 SOCKS5 代理,平均存活時間不到 15 分鐘。維護一個 500 個代理的輪換池,意味著每小時要消耗數千個 IP,而其中 80% 的請求不是逾時就是回傳 403。頻寬不可靠、延遲飆升常見,而且許多免費代理會注入廣告或修改回應內容。付費住宅代理網路(例如 Bright Data、Oxylabs)提供 95% 以上的成功率與黏性連線選項,但成本為每 GB 10 到 20 美元。從規模來看,只有當你需要繞過高價值目標的 IP 封鎖時,住宅代理的數學才划算。對於其他所有情況,一個乾淨的 IP 搭配適當的請求節奏,遠比混亂的免費代理池更有效。
輪換何時真正有效
代理輪換只對一種特定威脅有效:每個 IP 各自重置的 IP 速率限制。如果網站使用簡單的 X-Forwarded-For 檢查或每個 IP 的令牌桶,那麼每次請求後輪換 IP 就能繞過限制。這種情況常見於較小的電子商務網站與從未更新機器人偵測的舊版 API。在這些情況下,即使是免費代理池也能運作——但前提是你必須實作重試邏輯,丟棄失敗的代理並快速循環使用新的代理。
以下是一個使用 requests 並搭配重試與輪換迴圈的簡短 Python 範例。它假設 proxy_list 中有一串代理 URL,以及一個目標 url:
import requests
from itertools import cycle
proxy_pool = cycle(proxy_list)
max_retries = 5
for attempt in range(max_retries):
proxy = next(proxy_pool)
try:
resp = requests.get(
url,
proxies={"http": proxy, "https": proxy},
timeout=10,
headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..."}
)
if resp.status_code == 200:
break
except (requests.ConnectionError, requests.Timeout):
continue
else:
raise RuntimeError("All proxies failed")
這個模式只有在網站的偵測純粹基於 IP 時才有效。在請求之間加上 time.sleep(random.uniform(1,3)) 來模仿人類的節奏。對於執行 Turnstile 或 Datadome 的網站,這段程式碼每次都會失敗——無論使用哪個代理,挑戰頁面都會回傳 403 或 CAPTCHA。在這種情況下,你需要的是帶有真實指紋的無頭瀏覽器,而不是輪換 IP 清單。
黏性連線——在相關的一組請求中保持同一個 IP——通常比每次請求都輪換更有效。許多電子商務網站期望瀏覽連線使用單一 IP(例如將商品加入購物車、結帳)。在連線中途輪換會觸發詐欺標記。使用代理池,但為每個連線分配一個 IP,而不是每個請求。免費代理很少支援黏性連線,因為同一個 IP 會被多位使用者重複使用;你會看到連線資料交叉污染。付費住宅代理提供與自然瀏覽行為一致的黏性連線持續時間(5 到 30 分鐘)。
只有在了解目標的偵測堆疊時,才選擇輪換。先使用單一 IP 測試。只有在遇到速率限制時才加入輪換。而且永遠不要依賴免費代理進行正式生產——它們的失敗率會讓你在工程時間與資料損失上付出比便宜的住宅方案更高的代價。