Kazumi 深度解析:一個 XPath 選擇器如何撐起整個番劇宇宙

影音播放 ·约 10 分钟 ·記筆記的貓:團子

Kazumi 是什麼

如果你曾經在手機上追番,一定遇到過這種情況:今天某個站能用、明天另一個站又掛了,播放器裝了三個、每個都只能覆蓋幾個來源。Kazumi 就是為解這個問題而生的——它不是一個播放器,而是一個規則驅動的番劇採集器。

用一句話概括它的工作方式:你告訴它某個番劇網站的頁面結構(用九個 XPath 選擇器),它就能自動從那個站搜尋番劇、抓取播放連結、交付給內建播放器。來源掛了?換一條規則就好,不用換 App。這套設計讓 Kazumi 目前累積了數百條社群規則,覆蓋絕大多數主流番劇站。

Kazumi 基於 Flutter 開發,在 Android 上是原生 APK,也有 Windows、macOS、Linux 的桌面版本。播放器核心用的是 media-kit,硬解與超分辨率都走 GPU 路線,加上即時彈幕、跨裝置雲端同步追番進度——它把「追番」這件事需要的所有環節都打包進了一個 30MB 左右的安裝包。

安裝

Android 用戶最簡單,從 Kazumi下載 拿 APK 直接安裝即可——包名 com.predidit.kazumi,最新版 2.2.7,安裝包約 30MB。第一次啟動需要給存儲權限(用來下載離線番劇和緩存規則),如果要開啟彈幕和追番提醒,還要開通知權限。

桌面端的情況比較有意思。Linux 官方標註為「實驗性」,但分發管道反而最多——想要系統托盤圖示,必須選 deb 包,tar.gz 版本先天缺失這個功能;Arch 用戶可以從 AUR 裝 kazumi 或 kazumi-bin;非 Debian 系則走 Flathub 的 Flatpak(應用 ID io.github.Predidit.Kazumi)。macOS 和 Windows 都是單一安裝包,一路下一步即可。

裝好之後的第一個動作:進入「我的 → 規則管理」,點右上角「+」選擇規則倉庫,從社群同步幾條熱門規則進來——這是最省時的起步方式。

九個欄位的真相:為什麼是 XPath,而不是 JSON 配置

第一次打開規則編輯器時,你會看到九個空欄位,按兩排排列。上排是 Name、Version、BaseURL 三個身份欄位,下排是 SearchURL、SearchList、SearchName、SearchResult、ChapterRoads、ChapterResult 六個 XPath/JSONPath 欄位。官方文檔把它們拆成獨立頁面逐一講解,但我想從架構層面回答一個問題:為什麼是這九個欄位?為什麼選 XPath 而不是更現代的 CSS 選擇器或 JSONPath?

一條數據流的旅程

先看一條規則從被調用到把影片交給播放器,中間經歷了什麼:

SearchURL(帶 @keyword 占位符)
    ↓ 把 @keyword 換成你搜的番名,應用發 HTTP 請求
SearchList(XPath)──→ 框住搜索結果頁上所有番劇卡片
    ↓ 對每一條結果
SearchName(XPath)──→ 取出番剧名
SearchResult(XPath)──→ 取出該劇的詳情頁連結
    ↓ 用 SearchResult 的連結去請求下一個頁面
ChapterRoads(XPath)──→ 框住播放頁的所有線路(「線路一、線路二…」)
    ↓ 對每條線路
ChapterResult(XPath)──→ 取出該線路下的所有集數連結
    ↓ 你點某一集,應用嗅探視頻流,交給 media-kit 播放器

這條鏈有三個關鍵設計決策:

決策一:SearchResult 是整條鏈的樞紐。 它的 XPath 決定了後面 ChapterRoads 和 ChapterResult 面對的是什麼頁面——如果 SearchResult 指向詳情頁,後兩個欄位就在詳情頁 DOM 裡找線路和集數;如果 SearchResult 直接指向播放頁,後兩個欄位就改在播放頁裡找。這種「繼承式定位」讓同一個規則引擎可以適應兩種網站結構,而不需要寫兩套不同的規則格式。新手最容易在這裡踩坑——以為每個 XPath 都獨立運作,實則它們是串聯的。

決策二:XPath 而非 CSS 選擇器。 CSS 選擇器在現代前端開發裡更常見,但 XPath 能做一件 CSS 做不到的事——按文本內容反向定位。舉例來說,你可以寫 //div[contains(text(),'播放')] 找到頁面裡所有包含「播放」字樣的 div,然後往上走兩層拿到它的父容器。CSS 選擇器沒有 contains(text(),...) 這樣的能力,只能按 class、id、屬性值來定位。對於番劇站這種大量使用動態 class(比如 class="a1b2c3-drama-card")、結構又不斷變化的場景,XPath 的文本匹配反而是最穩定的錨點。

決策三:兩條鏈可獨立選擇規則類型。 SearchURL 到 SearchResult 這半段可以用 XPath,ChapterRoads 到 ChapterResult 那半段可以換成 API(JSONPath),或者全程只用一種。這個設計的聰明之處在於——很多番劇站的搜索結果頁是服務端渲染的 HTML(XPath 好用),但播放頁的視頻流列表是前端用 JavaScript 從 API 拉的 JSON(XPath 派不上用場,JSONPath 合適)。把規則類型拆成兩段,就不需要在「全站規則」和「API 規則」之間二選一。

XPath 的取捨:強大但不完整

Kazumi 對 XPath 的支援是有選擇性的——官方文檔明說「只支援以 // 開頭的選擇器」。這意味著你不能用 /html/body/div[3] 這樣的絕對路徑(太脆弱,頁面一改動就全廢),必須退回到相對路徑,然後靠 @class、@id 或 contains(text(),...) 來定位。

這個限制初看像是功能殘缺,實則是防呆設計。完整 XPath 支援包含了軸(ancestor::、following-sibling::)、函式鏈、條件表達式等複雜語法,新手容易寫出能跑但極難維護的規則。只支援 // 開頭 + 基本函式,強制所有人用最簡單直白的方式定位——社群規則的可讀性因此有了底線。

為什麼一個 30MB 的 App 能跟上幾百個站點?

Kazumi 的規則不是硬編碼進 APK 的。它有一套規則倉庫機制——你從社群同步的規則存在本地 JSON 裡,應用啟動時讀入記憶體的一個 Map。當你搜尋某部番劇時,應用會遍歷所有已啟用規則,用每條規則的 SearchURL 發請求,第一條返回結果的規則勝出。

這種「規則即資料、資料從社群來」的架構,是 Kazumi 能跟得上幾百個站點更新節奏的核心原因。App 本身不用發版更新——社群用戶發現某個站的 DOM 變了,改一下 SearchList 的 XPath 提交 PR,幾小時內所有用戶就能同步到新規則。

但這套架構也有代價。規則寫得好不好,完全取決於寫規則的那個人對目標站 DOM 結構的理解。一條寫得不好的規則(比如 XPath 選了一個會隨機變的 class)會在幾天之內失效,而且失效時不會有任何報錯——應用只會在搜尋時返回空結果。這就是為什麼 Kazumi官網 反覆強調:寫完規則一定要實際搜一部番驗證,不要攒一堆失效規則。

彈幕、追番提醒與雲端同步:那些被低估的工程細節

彈幕功能看似簡單——播放器上層疊一層透明畫布,把彈幕文字按時間軸繪製上去。但 Kazumi 做了一個關鍵優化:彈幕資料在播放開始前一次性下載完成,然後在本地做時間同步。這避免了在播放過程中頻繁請求彈幕伺服器導致的畫面掉幀,也讓離線播放時彈幕同樣可用。GPU 硬解 + 超分率走的是 media-kit 的原生路線,在 Android 上表現差異取決於芯片組(驍龍 8 Gen 2 往上基本穩),桌面端則取決於顯卡驅動——NVIDIA 閉源驅動下如果超分不生效,需要在設置裡把解碼器從預設切到 MediaCodec。

跨裝置同步追番進度走的是 WebDAV 協定——用戶自己填伺服器地址、帳密,應用把追番清單和觀看進度上傳到那裡。這不是什麼高大上的雲端方案,但它解決了一個真實場景:你在手機上看到第 12 集、晚上回到家在電腦上接著看,不用再回憶上次看到哪裡。追番提醒則靠的是本地排程——應用維護一份追番列表,定時檢查對應規則的搜索結果裡有沒有新集數,有就發系統通知。

誰適合、誰不適合

如果你願意花半小時學一下 XPath 基礎,或者只是想裝上 App 然後同步社群規則直接用,Kazumi 是目前開源番劇工具裡架構最清晰、擴展性最好的選擇。它的規則驅動設計意味著只要社群還在貢獻規則,就沒有「這個站不能看了」這種問題——換一條規則就好。

但如果你只是想裝上播放器、什麼都不想管,Kazumi 可能會讓你覺得麻煩。規則編輯器的九個空欄位對純新手來說確實有門檻——雖然你可以完全依賴社群規則,但理解它們怎麼運作能幫你更快定位問題(比如某個站突然搜不到了,大概率是那條規則的 SearchList XPath 需要更新)。

總結一句話:Kazumi 把「番劇站聚合」這件事做成了一個可編程的規則引擎,而不是一個寫死來源的播放器。這個設計取捨讓它的天花板極高,也讓它的入門門檻比同類工具高一點——但只要跨過那道門,你得到的是一個真正屬於自己的番劇宇宙。

規則編輯器界面

播放器控制界面

本文為個人基於網路公開資料整理編寫,非官方內容,僅供學習交流參考;軟體版權歸原作者所有,請支持正版;使用過程中遇到問題請查閱官方管道,本文不承擔任何責任。

團子還試過這些