【新聞挖掘工坊:第 4 篇】資料清洗 XYZZ:去重、填空、時間換算
媒體網站邏輯千變萬化的首次衝撞
那天下午,我和書慶大哥興致勃勃地打開程式,想一次抓下三立、中央社、年代等好幾家大媒體的新聞。結果才發現,每家新聞站的 DOM 結構、標籤命名和資料所在的位置全都不一樣。三立的文章主體包在一個叫 .caas-body 的 <div> 裡;中央社卻把正文拆成多個 <p>,而且圖文排版還要點擊「顯示更多」才能拿到。
我們一開始天真地想用同一套函式去抓所有網站,卻發現程式一次又一次卡在錯誤上──404、空回應、甚至抓到一堆廣告區塊。每次重整 selector,就像換不同鑰匙嘗試打開同一把鎖,煞是折磨。書慶大哥鼓勵我:「阿聖,不要慌,先針對每家網站寫個小模組,再把共用邏輯抽出來。」這句話讓我靈光一現,決定採取「一站一專案、模組化設計」的策略。
後來,我們為每家媒體都各自建立了 scraper class,裡頭只要覆寫一兩行 selector,就能輕鬆支援新網站。這樣一來,不但維護成本降低,也更容易在遇到網站改版時,快速定位並修正錯誤。從此,我們不再被千家媒體的奇葩結構打得措手不及,而是用模組化的理性,逐一破解它們的「假網站邏輯」。
關鍵字洪流與重複資料煩惱
當我們接著用一大串刑案關鍵字去跑 Google News,結果瞬間撈到上萬筆資料,每個關鍵字都會撈到同一則新聞,像是在資料湖裡發現了洶湧的重複浪潮。初版程式只簡單把 CSV 合併後執行 drop_duplicates(),卻仍看到數不清的重複標題殘留。
我跟書慶大哥討論後,才意識到新聞標題的小改動就會被視為不同條目,例如「嫌犯落網」與「嫌犯被捕」根本是同一事件,卻被重複計算。於是,我們取出了標題欄位做 NFKC 正規化、去除標點與大小寫差異,接著再用 fuzzywuzzy 計算相似度,只要相似度高於 90%,就合併為同一筆。
透過這套「文本標準化+模糊比對」方案,重複率瞬間下降 30%,整體數據才回歸到真實的 20,000 筆左右。這次經驗告訴我們:遇到「看似簡單的去重」時,其實背後包含了語言多樣性與編輯風格的挑戰,需要更精細的文字處理邏輯,才能真正把雜音剔除乾淨。
被 Google 擋 IP 的狼狽一刻
有次上課時,我的電腦同步在背後狂刷 Google News,結果不到十分鐘就被回「429 Too Many Requests」,甚至出現連不上網的 DNS 錯誤。整個 IP 被 Google 新聞臨時封鎖,完全暫停了我們的抓取作業。那一刻,我跟書慶大哥只能苦笑:「看來我們太急性子了,要被反爬機制請出去喝茶了。」
為了避開這種阻斷,我們先改成 requests.Session() 保持連線然後隨機更換 User-Agent,還加了 sleep(random.uniform(1,5)) 的隨機延遲。接著,我們接了一台VPN跳板,以不同 IP 輪流發送請求,並且在遇到 429 時立刻退避 60 秒再重試。這些策略讓程式訪問看起來更「像人」,避免了短時間內的過度流量。
結果神奇地奏效了:原本被擋的狀況大幅減少,爬蟲也能穩定運行一整晚。透過這次教訓,我們才徹底學會「要尊重伺服器,就用人性的節奏去爬」,而非只靠程式的野蠻硬刷。
標題過濾精度不夠:從標題到內容的轉身
開始用 NLP 模型過濾新聞時,我先嘗試只用標題就做分類,想說速度快、成本低。但實際上,標題往往過於簡短或過度用標點「吸睛」,導致模型判斷的精準度不到一半。某些含「判決」二字的標題,明明跟警察無關,卻被誤歸入刑案新聞裡。
我和書慶大哥一起翻了翻程式碼,發現只比對標題太粗糙。於是我們決定改為先用爬蟲把文章內容都抓下來,再用 NLP 模型做全文分類。雖然這樣推論成本高,但每次只需要對抽樣後的千篇文章做推論,就能大大提升準確度。為讓本地硬體負擔更小,我們把內容分析分成多個子程序,每次只跑一百篇,完成後再合併結果。
改成全文分類後,精準度立刻衝破 80%。這次轉身告訴我們:找對「情報來源」比只靠表面關鍵字更重要,有時候要捨棄省時方案,才能換得更可靠的數據。
NameError 與協作模組化
有一次在 Notebook 裡,程式在跑到 strptime 時直接跳出 NameError: datetime is not defined,眼看跑了大半夜就這麼崩了。當下真是哭笑不得:平常在片段測試時都 import 得好好的,一整合到主腳本就忘記了。
那晚我和書慶大哥決定大改架構,把各個功能拆成獨立模組:RSS 抓取、重導向解析、資料清洗、時間轉換、NLP 分析。每個模組都在最上方統一 import 所需套件。只要主腳本 import news_scraper,就能確保所有依賴一次到位,避免像那樣漏掉任何 import。
這套模組化設計,讓我們在協作時不再因為少一行 import 而全盤皆錯,也讓後續新功能開發更流暢。從此以後,每次出現 NameError,先到對應模組裡看看有沒有漏 import,就能快速排除錯誤。
破解進階搜尋與分段策略的救贖
終於,我們在一次早晨茶歇後,想到把「進階搜尋參數」放回到 RSS 抓取階段:after:2024-01-01 before:2024-04-30。這讓我們在源頭就把時間範圍精準篩掉大半雜訊,只要後段再做少量檢查就好。配合之前的分批存檔,每 500 筆就寫一次 CSV,斷點重跑完全不用擔心資料遺失。
這種「源頭過濾+分段輸出」的策略,讓整個流程效率暴增,也避免了後續重複清洗與繁瑣的重跑。當我們看到最後一次爬到 28,000 多筆後,去重完只剩下 20,000 筆,心裡那種成就感,就像淘礦者篩出了一籃籃純淨的金砂。
回首來路,正是那些「失敗一次又一次,卻不斷嘗試新方法」的日子,才讓我們最終走到成功的彼岸。這場與書慶大哥的合作之旅,充滿挑戰,也充滿歡笑。也許下次,你也能在自己的資料探勘工坊裡,創造出同樣燦爛的挖掘時刻。
Comments
Post a Comment