33 KiB
爬蟲 API 規格說明
本文件說明如何實作一個 Spider 爬蟲,包含所有方法的參數、回傳格式及 JSON 結構定義。
目錄
概覽
Spider 是應用程式爬蟲的抽象基底類別,位於 com.github.catvod.crawler.Spider。每個影片來源(Site)對應一個 Spider 實例。
生命週期:
init(context, ext)
│
├─► homeContent(filter) 首頁分類
├─► homeVideoContent() 首頁推薦
├─► categoryContent(...) 分類瀏覽
├─► detailContent(ids) 影片詳情
├─► searchContent(key, quick) 搜尋
├─► playerContent(flag, id, ...) 播放解析
├─► liveContent(url) 直播解析
└─► destroy() 清理資源
欄位:
| 欄位 | 類型 | 說明 |
|---|---|---|
siteKey |
String |
由載入器注入,標識此 Spider 服務的來源 key。 |
爬蟲類型與載入方式
在 sites 配置中,type 欄位決定呼叫方式,api 欄位決定載入哪種引擎:
type |
api 格式 |
引擎 | 說明 |
|---|---|---|---|
0 |
HTTP URL | 內建 XML 解析 | 直接 GET 請求,回傳 XML 格式。 |
1 |
HTTP URL | 內建 JSON+Filter | 直接 GET 請求,回傳 JSON 格式,篩選參數以 f= 傳遞。 |
3 |
csp_ClassName |
JAR(DexClassLoader) | 從 jar 指定的 .jar 檔載入 com.github.catvod.spider.ClassName。 |
3 |
xxx.js |
JavaScript(QuickJS) | 載入 .js 檔作為 Spider。 |
3 |
xxx.py |
Python(Chaquopy) | 載入 .py 檔作為 Spider。 |
4 |
HTTP URL | 內建 JSON+Base64 ext | 同 1,擴充參數以 Base64 編碼傳遞(ext=)。 |
本文件主要說明
type=3(Spider 直接呼叫)的情境。
Spider 抽象類別
所有方法預設回傳空字串 "",子類別僅需覆寫所需功能。
init — 初始化
public void init(Context context, String extend) throws Exception
觸發時機: Spider 實例建立後呼叫一次,用於初始化連線、載入設定等。
| 參數 | 類型 | 說明 |
|---|---|---|
context |
Context |
Android Context,可取得應用資源、路徑等。 |
extend |
String |
對應 Site.ext 欄位的額外擴充資料,內容由爬蟲自行定義(可為 URL、JSON 字串或路徑)。 |
回傳: 無(void)
homeContent — 首頁分類
public String homeContent(boolean filter) throws Exception
觸發時機: 使用者進入首頁時呼叫,取得分類列表(及可選的篩選器)。
| 參數 | 類型 | 說明 |
|---|---|---|
filter |
boolean |
true 表示需要回傳篩選器資料(filters 欄位)。 |
回傳: JSON 字串,結構為 Result。
class(分類列表)為主要回傳欄位,filters(各分類的篩選器定義)為選填。
homeVideoContent — 首頁推薦影片
public String homeVideoContent() throws Exception
觸發時機: 首頁分類載入完成後呼叫,取得首頁推薦影片列表。
回傳: JSON 字串,結構為 Result。
主要回傳欄位為 list(推薦影片列表)。
categoryContent — 分類列表
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception
觸發時機: 使用者點擊分類或切換篩選條件時呼叫。
| 參數 | 類型 | 說明 |
|---|---|---|
tid |
String |
分類 ID,對應 Class.typeId。 |
pg |
String |
頁碼,從 "1" 開始。 |
filter |
boolean |
是否啟用篩選器。 |
extend |
HashMap<String, String> |
使用者選擇的篩選條件,key 為篩選器 ID,value 為選項 key。為空 {} 時表示無篩選。 |
回傳: JSON 字串,結構為 Result。
主要回傳欄位為 list(影片列表),選填 pagecount(總頁數,用於分頁控制)。
detailContent — 影片詳情
public String detailContent(List<String> ids) throws Exception
觸發時機: 使用者點擊影片卡片時呼叫,取得完整詳情與播放集數。
| 參數 | 類型 | 說明 |
|---|---|---|
ids |
List<String> |
影片 ID 清單,通常只含一個元素,對應 Vod.vodId。 |
回傳: JSON 字串,結構為 Result,list 陣列中有一個完整的 Vod 物件。
主要 Vod 欄位:vod_id、vod_name、vod_play_from、vod_play_url。
searchContent — 搜尋
public String searchContent(String key, boolean quick) throws Exception
public String searchContent(String key, boolean quick, String pg) throws Exception
觸發時機: 使用者輸入關鍵字搜尋時呼叫。
| 參數 | 類型 | 說明 |
|---|---|---|
key |
String |
搜尋關鍵字。框架會自動進行繁→簡轉換以提升相容性。 |
quick |
boolean |
true 表示快速搜尋(只傳回基本資訊),false 表示完整搜尋。 |
pg |
String |
頁碼(僅分頁版本),從 "1" 開始。 |
回傳: JSON 字串,結構為 Result。
主要回傳欄位為 list(搜尋結果影片列表)。
若
Site.quickSearch = 0,快速搜尋會被跳過,直接回傳空結果。
playerContent — 播放解析
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception
觸發時機: 使用者選擇集數準備播放時呼叫,需解析出實際的媒體 URL。
| 參數 | 類型 | 說明 |
|---|---|---|
flag |
String |
播放來源名稱,對應 vod_play_from 中的一項(如 "youku"、"iqiyi")。 |
id |
String |
集數 URL 或 ID,對應 vod_play_url 中某集數的 value 部分。 |
vipFlags |
List<String> |
全局 VIP 平台旗標清單,對應配置中的 flags 欄位(如 ["qq", "youku"])。 |
回傳: JSON 字串,結構為 Result(播放解析結果)。
主要回傳欄位為 url(實際可播放的媒體 URL)。
選填欄位:
| 欄位 | 說明 |
|---|---|
parse |
0 = 直接播放,1 = 需進一步解析(預設 0)。jx=1 效果相同。 |
jx |
同 parse=1,需進一步解析。 |
playUrl |
解析器前綴或指定。json:… 傳入 JSON 解析器,parse:解析器名稱 指定具名解析器,其他值作為解析 URL 前綴。 |
click |
點擊攔截處理 URL,傳遞給解析器 WebView。 |
code |
非零時抑制 msg 顯示。 |
header |
播放請求所需的 HTTP 標頭(鍵值對)。 |
flag |
覆蓋來源旗標,傳入 VIP 解析器時使用。 |
jxFrom |
強制指定解析器旗標(覆蓋 flag 的解析器比對結果)。 |
format |
媒體 MIME type(如 "application/x-mpegURL"),指定後播放器跳過格式自動偵測。 |
danmaku |
彈幕資料列表,詳見 Danmaku。 |
subs |
字幕列表,詳見 Sub。 |
drm |
DRM 版權保護設定,詳見 Drm。 |
artwork |
播放頁面封面圖 URL。 |
desc |
播放頁面描述文字。 |
position |
播放恢復位置(毫秒)。 |
liveContent — 直播頻道列表
public String liveContent(String url) throws Exception
觸發時機: 載入直播來源時呼叫,爬蟲回傳頻道列表的原始文字,框架再依格式解析(支援 TXT、M3U、JSON)。
| 參數 | 類型 | 說明 |
|---|---|---|
url |
String |
來源配置中的 Live.url 欄位值。 |
回傳: 頻道列表的原始文字字串(非 JSON Result),格式可為:
| 格式 | 說明 |
|---|---|
| TXT | 每行 頻道名稱,URL#URL2...,以 #genre# 分組。 |
| M3U | 標準 #EXTM3U/#EXTINF 格式。 |
| JSON | Group 物件陣列,結構與配置的 groups 欄位相同。 |
proxy — 本地代理
public Object[] proxy(Map<String, String> params) throws Exception
觸發時機: 應用程式內建本地 HTTP 代理伺服器收到請求時呼叫。
| 參數 | 類型 | 說明 |
|---|---|---|
params |
Map<String, String> |
代理請求參數,從本地代理 URL 的 query string 解析而來。通常含有 do、url 等自定義參數。 |
回傳: Object[](注意:非 JSON 字串),格式為:
// 200 正常回應
Object[] {
Integer statusCode, // HTTP 狀態碼(200)
String mimeType, // Content-Type(如 "video/mp2t")
InputStream body // 回應內容
}
// 302 重定向
Object[] {
Integer statusCode, // 302
String mimeType, // "text/plain"
InputStream body, // 通常為空或提示文字
Map<String, String> headers // 含 "Location" key 的重定向標頭
}
action — 自定義動作
public String action(String action) throws Exception
觸發時機: UI 層呼叫特定自定義指令時呼叫(如登入、重新整理 Token 等)。action 字串格式由爬蟲自行定義,框架不解析其內容。
| 參數 | 類型 | 說明 |
|---|---|---|
action |
String |
動作指令字串,格式由爬蟲自行定義。 |
回傳: JSON 字串,結構為 Result。
manualVideoCheck / isVideoFormat — 影片格式判斷
public boolean manualVideoCheck() throws Exception
public boolean isVideoFormat(String url) throws Exception
| 方法 | 說明 |
|---|---|
manualVideoCheck() |
回傳 true 時,框架在 WebView 中攔截 URL 後會呼叫 isVideoFormat() 進行人工判斷。 |
isVideoFormat(url) |
判斷指定 URL 是否為有效的直接媒體 URL。回傳 true 表示可直接播放。 |
參數(isVideoFormat) |
類型 | 說明 |
|---|---|---|
url |
String |
待判斷的 URL。 |
destroy — 銷毀
public void destroy()
觸發時機: 配置重新載入或應用程式清理快取時呼叫,釋放資源(連線、執行緒等)。
回傳: 無(void)
回傳資料結構
所有方法(proxy 除外)的回傳值均為 JSON 字串,解析後對應以下物件。
Result — 通用回傳物件
不同方法使用的欄位不同,以下按方法分組說明。
homeContent:
| JSON 欄位 | 類型 | 說明 |
|---|---|---|
class |
array<Class> |
分類列表。詳見 Class。 |
filters |
object |
篩選器定義,key 為 type_id,value 為 Filter 陣列。詳見 Filter。 |
homeVideoContent / categoryContent / detailContent / searchContent:
| JSON 欄位 | 類型 | 說明 |
|---|---|---|
list |
array<Vod> |
影片卡片列表。詳見 Vod。 |
pagecount |
integer |
總頁數(categoryContent、searchContent 使用)。 |
playerContent:
| JSON 欄位 | 類型 | 說明 |
|---|---|---|
url |
string |
實際播放媒體 URL。 |
parse |
integer |
0 = 直接播放,1 = 需進一步解析(預設 0)。jx=1 效果相同。 |
jx |
integer |
同 parse=1,需進一步解析(兩者任一為 1 即觸發解析流程)。 |
playUrl |
string |
解析器前綴或指定。json:… 傳入 JSON 解析器,parse:解析器名稱 指定具名解析器,其他值作為解析 URL 前綴。 |
key |
string |
來源 key,用於從配置查找對應 Site.click。當爬蟲未回傳 click 時,框架以此 key 從 VodConfig 取得 click。 |
click |
string |
點擊攔截處理 URL,傳遞給解析器 WebView 執行點擊動作。 |
code |
integer |
非零時抑制 msg 顯示(通常用於錯誤狀態碼)。 |
header |
object |
播放請求的額外 HTTP 標頭,鍵值對格式。 |
flag |
string |
播放來源旗標名稱,覆蓋原始 flag 參數。 |
jxFrom |
string |
強制指定解析器旗標(覆蓋 flag 的解析器比對結果)。 |
format |
string |
媒體 MIME type(如 "application/x-mpegURL"、"application/dash+xml"),指定後播放器跳過格式自動偵測。 |
danmaku |
array<Danmaku> |
彈幕資料列表,詳見 Danmaku。 |
subs |
array<Sub> |
字幕列表,詳見 Sub。 |
drm |
Drm |
DRM 版權保護設定,詳見 Drm。 |
artwork |
string |
播放頁面封面圖 URL。 |
desc |
string |
播放頁面描述文字。 |
position |
long |
播放恢復位置(毫秒)。 |
lrc |
string |
歌詞 URL(音樂類來源使用)。 |
通用欄位(所有 JSON 回傳方法):
| JSON 欄位 | 類型 | 說明 |
|---|---|---|
msg |
string |
錯誤或提示訊息。 |
Vod — 影片卡片物件
list 陣列中的每個元素。
| JSON 欄位 | 類型 | 說明 |
|---|---|---|
vod_id |
string |
影片唯一 ID,傳入 detailContent 的 ids 參數。 |
vod_name |
string |
影片顯示名稱(支援 HTML 編碼)。 |
vod_pic |
string |
縮圖 URL。 |
vod_remarks |
string |
備註標籤,顯示在縮圖上(如 "更新至12集"、"HD")。 |
type_name |
string |
所屬分類名稱(用於分類過濾)。 |
vod_year |
string |
年份。 |
vod_area |
string |
地區。 |
vod_director |
string |
導演。 |
vod_actor |
string |
演員。 |
vod_content |
string |
簡介/描述。 |
vod_play_from |
string |
播放來源名稱,多個來源以 $$$ 分隔。 |
vod_play_url |
string |
播放集數 URL,格式詳見下方說明。 |
vod_tag |
string |
特殊標記。"folder" 表示此項為資料夾,點擊後以 vod_id 作為 tid 呼叫 categoryContent 取得子列表。 |
action |
string |
自訂動作字串,點擊時優先於資料夾行為觸發。type==3 呼叫 Spider.action(action),type==4 發送 HTTP 請求,結果以 Toast 顯示。 |
cate |
Cate |
資料夾顯示樣式物件,包含 land、circle、ratio 三個子欄位(含義同下方三欄)。設定此欄位等同於 vod_tag: "folder",即自動將此項視為資料夾。 |
land |
integer |
橫向顯示旗標,覆蓋 Class 層級的 land 設定。 |
circle |
integer |
圓形顯示旗標,覆蓋 Class 層級的 circle 設定。 |
ratio |
float |
卡片寬高比,覆蓋 Class 層級的 ratio 設定。 |
style |
Style |
此影片卡片的顯示樣式覆蓋,詳見 CONFIG.md。 |
Class — 分類物件
class 陣列中的每個元素。
| JSON 欄位 | 類型 | 說明 |
|---|---|---|
type_id |
string |
分類唯一 ID,傳入 categoryContent 的 tid 參數。可縮寫為 id。 |
type_name |
string |
分類顯示名稱。可縮寫為 name。 |
type_flag |
string |
"1" 表示此分類為資料夾類型。 |
land |
integer |
此分類下影片的橫向顯示旗標。 |
circle |
integer |
此分類下影片的圓形顯示旗標。 |
ratio |
float |
此分類下影片卡片的寬高比。 |
Filter — 篩選器物件
filters 為一個物件,key 為 type_id,value 為 Filter 陣列,每個 Filter 定義一個篩選維度。
{
"filters": {
"1": [
{
"key": "area",
"name": "地區",
"value": [
{"n": "全部", "v": ""},
{"n": "大陸", "v": "大陸"},
{"n": "美國", "v": "美國"}
]
},
{
"key": "year",
"name": "年份",
"value": [
{"n": "全部", "v": ""},
{"n": "2024", "v": "2024"}
]
}
]
}
}
Filter 欄位:
| JSON 欄位 | 類型 | 說明 |
|---|---|---|
key |
string |
篩選器 ID,作為 categoryContent 的 extend 參數的 key。 |
name |
string |
篩選器顯示名稱。 |
init |
string |
預設選中的選項 value(選填)。 |
value |
array |
可選項目列表,每項含 n(顯示名稱)與 v(傳入值)。 |
使用者選擇後,extend 傳入格式為:
{
"area": "大陸",
"year": "2024"
}
Danmaku — 彈幕物件
danmaku 陣列中的每個元素。
| JSON 欄位 | 類型 | 說明 |
|---|---|---|
url |
string |
彈幕來源 URL(必填),支援本地路徑(/ 開頭)。 |
name |
string |
顯示名稱(選填),省略時使用 url。 |
Sub — 字幕物件
subs 陣列中的每個元素。
| JSON 欄位 | 類型 | 說明 |
|---|---|---|
url |
string |
字幕檔 URL(必填)。 |
name |
string |
顯示名稱(選填)。 |
lang |
string |
語言代碼(選填,如 "zh-tw"、"en")。 |
format |
string |
MIME 類型(選填),常用值:"text/x-ssa"、"application/x-subrip"。省略時框架依副檔名自動偵測。 |
flag |
integer |
ExoPlayer C.SELECTION_FLAG_* 常數(選填)。0 或省略時預設為 SELECTION_FLAG_DEFAULT(自動選擇);2 = SELECTION_FLAG_FORCED(強制顯示,不可關閉)。 |
Drm — DRM 設定物件
drm 物件欄位。
| JSON 欄位 | 類型 | 說明 |
|---|---|---|
type |
string |
DRM 類型:"widevine"、"playready"、"clearkey"。 |
key |
string |
License Server URL(Widevine/PlayReady)或 ClearKey 金鑰字串。 |
header |
object |
License 請求的額外 HTTP 標頭,鍵值對格式(選填)。 |
forceKey |
boolean |
true = 強制使用預設 License URI(選填,預設 false)。 |
播放集數格式(vod_play_from / vod_play_url)
detailContent 回傳的 Vod 物件中,集數資訊以特定分隔符號編碼在兩個字串欄位中。
| 符號 | 用途 |
|---|---|
$$$ |
分隔多個播放來源(group) |
# |
分隔同一來源下的集數 |
$ |
分隔集數名稱與集數 URL |
範例:
vod_play_from: "線路一$$$線路二"
vod_play_url: "第01集$https://cdn1.example.com/ep1.m3u8#第02集$https://cdn1.example.com/ep2.m3u8$$$第01集$https://cdn2.example.com/ep1.m3u8#第02集$https://cdn2.example.com/ep2.m3u8"
對應解析結果:
線路一:
- 第01集 → https://cdn1.example.com/ep1.m3u8
- 第02集 → https://cdn1.example.com/ep2.m3u8
線路二:
- 第01集 → https://cdn2.example.com/ep1.m3u8
- 第02集 → https://cdn2.example.com/ep2.m3u8
集數 URL 的 value 部分即為 playerContent 的 id 參數。
完整 JSON 範例
homeContent 回傳範例
{
"class": [
{
"type_id": "1",
"type_name": "電影"
},
{
"type_id": "2",
"type_name": "電視劇"
},
{
"type_id": "3",
"type_name": "綜藝"
},
{
"type_id": "4",
"type_name": "動漫"
}
],
"filters": {
"1": [
{
"key": "area",
"name": "地區",
"value": [
{"n": "全部", "v": ""},
{"n": "大陸", "v": "大陸"},
{"n": "香港", "v": "香港"},
{"n": "台灣", "v": "台灣"},
{"n": "美國", "v": "美國"}
]
},
{
"key": "year",
"name": "年份",
"value": [
{"n": "全部", "v": ""},
{"n": "2025", "v": "2025"},
{"n": "2024", "v": "2024"}
]
}
]
}
}
homeVideoContent / categoryContent 回傳範例
categoryContent和searchContent可額外回傳pagecount;homeVideoContent無此欄位。
{
"list": [
{
"vod_id": "12345",
"vod_name": "範例電影",
"vod_pic": "https://example.com/pic/12345.jpg",
"vod_remarks": "HD",
"type_name": "電影"
},
{
"vod_id": "67890",
"vod_name": "範例電視劇",
"vod_pic": "https://example.com/pic/67890.jpg",
"vod_remarks": "更新至12集",
"type_name": "電視劇"
}
],
"pagecount": 10
}
detailContent 回傳範例
{
"list": [
{
"vod_id": "12345",
"vod_name": "範例電影",
"vod_pic": "https://example.com/pic/12345.jpg",
"vod_year": "2024",
"vod_area": "大陸",
"vod_director": "張三",
"vod_actor": "李四, 王五",
"vod_content": "這是一部精彩的電影...",
"vod_remarks": "HD",
"type_name": "電影",
"vod_play_from": "線路一$$$線路二",
"vod_play_url": "正片$https://cdn1.example.com/movie.m3u8$$$正片$https://cdn2.example.com/movie.m3u8"
}
]
}
多集電視劇範例:
{
"list": [
{
"vod_id": "67890",
"vod_name": "範例電視劇",
"vod_play_from": "主線路$$$備用線路",
"vod_play_url": "第01集$https://cdn1.example.com/ep1.m3u8#第02集$https://cdn1.example.com/ep2.m3u8$$$第01集$https://cdn2.example.com/ep1.m3u8#第02集$https://cdn2.example.com/ep2.m3u8"
}
]
}
playerContent 回傳範例
直接播放(無需解析):
{
"parse": 0,
"url": "https://cdn.example.com/video/ep1.m3u8",
"header": {
"User-Agent": "Mozilla/5.0",
"Referer": "https://www.example.com/"
}
}
需要進一步解析(VIP 影片):
{
"parse": 1,
"url": "https://www.youku.com/video/id_xxx.html",
"flag": "youku"
}
含字幕與彈幕:
{
"parse": 0,
"url": "https://cdn.example.com/video/ep1.m3u8",
"header": {
"Referer": "https://www.example.com/"
},
"subs": [
{
"name": "繁體中文",
"url": "https://cdn.example.com/sub/ep1.zh-tw.srt",
"lang": "zh-tw"
},
{
"name": "英文",
"url": "https://cdn.example.com/sub/ep1.en.srt",
"lang": "en"
}
],
"danmaku": [
{
"url": "https://danmaku.example.com/ep1.xml"
}
]
}
searchContent 回傳範例
{
"list": [
{
"vod_id": "12345",
"vod_name": "範例電影",
"vod_pic": "https://example.com/pic/12345.jpg",
"vod_remarks": "HD",
"type_name": "電影"
}
]
}
liveContent 回傳範例
TXT 格式:
央視頻道,#genre#
CCTV1,http://example.com/cctv1.m3u8#http://cdn2.example.com/cctv1.m3u8
CCTV2,http://example.com/cctv2.m3u8
台灣頻道,#genre#
TVBS,http://example.com/tvbs.m3u8
M3U 格式:
#EXTM3U
#EXTINF:-1 tvg-name="CCTV1" group-title="央視頻道",CCTV1
http://example.com/cctv1.m3u8
JSON 格式:
[
{
"name": "央視頻道",
"channel": [
{
"name": "CCTV1",
"urls": [
"http://example.com/cctv1.m3u8"
]
}
]
}
]
爬蟲本地代理 URL
爬蟲可在回傳的媒體 URL 中使用 proxy:// 協議,將請求導向本地代理伺服器,由對應語言的 proxy()
方法處理。這樣可以在播放器無法直接存取來源時,讓爬蟲居中轉發資料。
| 語言 | 回傳 URL 前綴 | 取得代理 URL 的方法 |
|---|---|---|
| Java(JAR) | proxy:// |
Proxy.getUrl(boolean local) |
| Python | proxy://?do=py |
getProxyUrl(boolean local) |
| JavaScript | proxy://?do=js |
getProxy(boolean local) |
local參數:true取得本地(127.0.0.1)代理位址,false取得可對外存取的 LAN IP 位址。
完整端點說明見 LOCAL.md — /proxy。
使用範例(Java):
@Override
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
String proxyUrl = Proxy.getUrl(true) + "?url=" + URLEncoder.encode(id, "UTF-8") + "&token=xxx";
return "{\"parse\":0,\"url\":\"" + proxyUrl + "\"}";
}
@Override
public Object[] proxy(Map<String, String> params) throws Exception {
String url = params.get("url");
String token = params.get("token");
InputStream stream = fetchWithAuth(url, token);
return new Object[]{200, "video/mp2t", stream};
}