【新聞挖掘工坊:第 3 篇】重導向大追蹤:怎樣從中繼頁跳到原始新聞?

HTTP 重導與瀏覽器跳轉的微妙差異

在網路世界裡,許多人誤以為只要發出一個 HTTP 請求,便能自動獲得最終落地頁的網址。事實上,伺服器端的 3xx 重導是在 HTTP 回應標頭裡告訴客戶端「去這裡」,像是常見的 301、302。任何能跟隨重導的 HTTP 客戶端(像是 cURL 或 Python 的 requests)都能自動跳轉到指定位置。這種行為是伺服器與客戶端之間的合作,雙方在協議層面就完成了重導。

然而,Google News RSS 條目所對應的中繼頁往往並非透過 3xx 回應來跳轉,而是依賴瀏覽器在客戶端執行的 JavaScript 或 <meta http-equiv="refresh"> 標籤。這些跳轉方式只有瀏覽器才會主動執行,純粹的 HTTP 客戶端並不會對內嵌的 <script> 或 <meta> 做任何動作,因此即便我們在 requests.get 裡打開 allow_redirects=True,也只能看到中繼頁的原始 HTML,無法取得真正的最終網址。

理解這兩者的差異,是進入「重導向大追蹤」的第一步。只有明白客戶端與伺服器端跳轉機制的分工,我們才能正確選擇工具:對於伺服器端的 3xx,自然交給 requests;而對於客戶端的 JS 跳轉,必須用能「執行」JavaScript 的瀏覽器環境,才能真正還原使用者在瀏覽器裡看到的結果。

初探 allow_redirects 與 response.history

當我們第一次學會用 requests.get(url, allow_redirects=True),都會驚呼這功能多麼便利。只要把參數打開,所有的 3xx 重導都能自動跟隨,而最終返回的 response.url 就是最終地址。而 response.history 更是一面鏡子,裡面列出每一階段的中間重導回應頭,讓開發者清楚看到整個跳轉過程。

帶著這期待,我們對 Google News RSS 條目發出請求,卻發現 response.history 始終是空的,response.url 也沒變動。種種跡象表明:這並非伺服器以 3xx 重導,而是瀏覽器端自己跑了 JS。於是,我們才恍然大悟,原來要跟隨的並非 HTTP 重導,而是瀏覽器在載入過程中「悄悄」操控的位置跳轉。

這段嘗試雖以失敗告終,卻讓我們更深入理解 HTTP 協議與瀏覽器行為的分界。之後,每當遇到類似情況,我們都會先查 response.history 判斷是否真的有 3xx,再決定要不要動用更重型的瀏覽器工具,避免平白浪費效能在不必要的 Selenium 啟動上。

HEAD 請求的嘗試與波折

為了進一步確認是否有任何重導資訊被藏在 HTTP 標頭裡,我們曾經嘗試用 requests.head(url) 只發出 HEAD 請求,讓伺服器回應 Location 標頭。然而,很快我們就發現,多數新聞站點對 HEAD 請求的支援並不完善,甚至直接回 405 Method Not Allowed,或者只回傳空標頭,讓我們毫無頭緒。

即便有些站點願意回 Location,得到的也往往是中繼頁面自身的地址,並非最終文章。這背後的原因在於,新聞網站常常不把跳轉資訊放在伺服器端,而是利用 JavaScript 和 iframe 等動態載入技術執行客戶端邏輯,完全不透過 Location 標頭。於是,HEAD 請求的方法很快就被我們列為「不可靠解法」。

這段經驗告訴我們:HTTP 標頭固然重要,但不是萬靈丹。面對複雜的跳轉機制,必須先了解站點的實際做法,再選擇是否採用 HEAD 或 GET。放棄這種做法後,我們轉向解析 HTML 內部靜態標籤,並在必要時呼叫瀏覽器,讓後續步驟更具彈性與魄力。

終極鑰匙:canonical 與 og:url

在多次失敗後,我們被一個 SEO 技巧所啟發:許多網站會在 <head> 裡放置 <link rel="canonical"> 或 <meta property="og:url">,告訴搜尋引擎這篇文章的「正式地址」。這些標籤靜靜地寫在 HTML 原始碼中,無論 JavaScript 如何跳轉,都不會被動態改寫,因此成為我們追蹤最終網址的最佳線索。

當我們改用 BeautifulSoup 解析中繼頁的 HTML,先查詢這兩個標籤的存在,就能快速取得原始網站的文章連結。這就如同拿到一把開門鑰匙,讓我們不用跑完整的跳轉流程,只要靜態抓取一次,就能獲得正確地址。實測顯示,能命中超過九成的最終網址,且無需任何繁瑣的重導嘗試。

當然,不是每個站點都會放 canonical 或 og:url,但這部分靜態資訊的可用性遠遠高於 HEAD 請求。我們把這套方法包成一個函式:先 parse HTML,找標籤;若沒找到,再 fallback 到 Selenium。如此一來,既保有效能,又能兼顧穩定度,讓整個爬蟲既輕巧又強大。

走入瀏覽器世界:Selenium 的必備時刻

當然,有少部分新聞站的跳轉完全依靠 JavaScript 動態載入,或是內部邏輯極其複雜,導致靜態解析 canonical也無法命中。在這種情況下,我們便會動用 Selenium headless Chrome。只要透過 WebDriver 啟動一個無頭瀏覽器,就能完全模擬使用者瀏覽流程:載入頁面、執行 JavaScript,再讀取 driver.current_url 與 driver.page_source

在實務中,我們會先設定條件等待(WebDriverWait),偵測到文章標題或某個特定 DOM 元素出現,才認定跳轉完成。這樣可以大幅減少等待時間,也能避免固定 sleep 所帶來的資源浪費。等到條件滿足後,便能確保自己抓到的 driver.current_url 就是真正的最終網址,不再被中繼頁或廣告插入誤導。

雖然 Selenium 的啟動開銷較大,但只要在靜態方法失敗時才呼叫,就能取得最妥善的結果。這段「瀏覽器世界」的插曲,不僅讓我們掌握了動態跳轉的終極解法,更教會了我們如何平衡效能與可靠性,避免單純靠瀏覽器就把整個流程拖垮。

完整流程串接:從 RSS 連結到原始新聞

將上述各種方法融會貫通後,我們最終建立了一條堅固的「重導向大追蹤」管線。首先,程式從 RSS 條目清單裡取出 entry.link,對其呼叫靜態解析函式,嘗試抓取 canonical 或 og:url。若解析成功,直接回傳最終網址;若失敗,才啟動 Selenium headless,執行 JavaScript,讀取 driver.current_url

如此一來,靜態抓取與動態瀏覽結合,最大程度節省運算資源。經過測試,這套流程能準確命中九成以上的原始新聞網址,同時大幅降低對伺服器的壓力,避免過度反爬。更重要的是,我們把這段邏輯封裝成模組化函式,日後只要更新標籤選擇器或等待條件,就能快速因應網站改版。

這條從 RSS 連結到最終 URL 的大追蹤之路,不僅是技術實踐的代表,也展現了「工具優先」、「靜動結合」與「Fail-safe 插拔」的底層思維。當讀者在下一篇文章中看到我們輕鬆地拿到正確連結,就能懂得這條看似平凡的管線,背後其實蘊含了多次失敗與反覆優化的心血結晶。

Comments

Popular posts from this blog

【新聞挖掘工坊:第 2 篇】Google News RSS 祕密通道:怎麼抓新聞連結?

【統計抽樣 × NLP 節能分析:第 3 篇】階層、系統、叢集:三大抽樣法一次搞懂

區域網路扁平架構與 Zero Trust 缺口:從 Streamlit 測試到 IoT 隔離的安全評估