我在一台 2 核 4G 的 VPS 上跑一個小腳本,每天把 HaoNews 熱榜的標題、連結、抓取時間存成一份 JSONL,累積大概三個多月,檔案 18 MB 左右、兩萬多筆。原本用 grep 找「關鍵字出現在哪些標題」,後來想按日期、來源做聚合,就換成 DuckDB。版本是 0.10 系列,Python 3.11,資料就是一個目錄下的 *.jsonl。
做法很簡單,先確認欄位:每行是 {"title":..., "url":..., "fetched_at":..., "source":...}。我用 view 直接讀目錄,不先建表:
CREATE VIEW hot AS
SELECT * FROM read_json_auto('data/*.jsonl',
format='newline_delimited',
columns={title:'VARCHAR', url:'VARCHAR',
fetched_at:'TIMESTAMP', source:'VARCHAR'});
查某個詞出現的日期分布:
SELECT CAST(fetched_at AS DATE) d, count(*) n
FROM hot
WHERE title ILIKE '%晶片%'
GROUP BY 1 ORDER BY 1;
兩萬筆跑起來大概零點幾秒,比我原本用 Python 逐行讀快很多,也不用先匯入資料庫。為什麼這樣做:read_json_auto 加 columns 是關鍵,我一開始沒指定型別,fetched_at 被推成 VARCHAR,日期聚合就變成字串排序,跨月就亂了。明確定義之後,DuckDB 只掃需要的欄位,18 MB 的檔案幾乎沒感覺。代價是每次查詢都要重新解析 JSONL,檔案再大就會痛;那時就得 CREATE TABLE ... AS SELECT 落地一次,或者轉 Parquet。
會失效的地方有幾個。第一,JSONL 裡只要有一行欄位缺漏或型別不對,read_json_auto 預設會直接報錯,整個 view 都查不了;我後來加 ignore_errors=true 才勉強能跑,但那些列就被跳過了,要自己另外數。第二,ILIKE 對中文沒問題,但對全形半形、異體字沒轍,我搜「台積」和「臺積」結果不一樣,這不是工具的錯,是我一開始沒想到。第三,source 欄位我當初沒統一,有的是網域、有的是站名,聚合出來很亂,現在只能先 lower() 再人工對照。
還有一個小坑:同一則熱點會被不同來源重複抓到,標題幾乎一樣但 url 不同,直接 count(*) 會高估熱度。我目前是用 title 去掉空白後 group 來粗略去重,但這方法對「標題被改寫」完全沒用。
你們在這種「每天累積一點、事後想查」的場景,是先把 JSONL 轉 Parquet 再查,還是每次直接掃原始檔?我現在卡在要不要早點轉檔。
0 条评论
还没有评论,写下第一条。