書源 JSON 對接指南
這份文件提供書源結構、必要欄位、功能區塊與常見配置方式。
可先快速開始建立骨架,再依 request / response 欄位說明完成來源支援的功能。
結構總覽
最外層只有 info 與 data。先確認骨架正確,再分別補上請求規則與解析規則。
快速開始#
新增書源時可依下列順序處理。每一步都對應後續章節,可先完成最小可用版本,再補齊特殊情境。
配 request 抓頁面
填 apiHost / apiPath / httpMethod 遇到 GBK 編碼時,調整
charEncodingRawValue
需要繞 Cloudflare 改 requestMethodRawValue: 1
配 response 解析資料
用 DevTools 找 CSS selector,先寫 listElement,再寫各
*Element。actionValue 決定取什麼。
若解析結果包含站台 host,可用 hostsToReplace
移除指定 host 字串,讓後續 request 使用相同的 path 格式。
實機驗證
將書源 JSON 匯入 App,先測基本流程:首頁、詳細頁、章節目錄、章節正文。
若來源有分類列表或搜尋時,再分別測試。
流程一覽#
每個書源建議先驗證「首頁 → 詳細 → 章節 → 正文」主流程。
分類與搜尋屬於額外功能,若網站不提供時,可省略對應規則。
同一段要同時檢查 request 與
response,因為 URL 組法會直接影響解析結果的路徑格式。
info:書源資訊#
info 書源基本資訊,例如書源名稱、是否允許搜尋、是否允許下載。index 建議使用 1000 以上避免與既有書源衝突。
| 欄位 | 型別 | 必填 | 用途 | 對接注意 |
|---|---|---|---|---|
type |
Int | 必 | 書源類型 | 自訂書源固定 99 |
name |
String | 必 | 顯示名稱 | 書源標題 |
alias |
String | 否 | 顯示別名 | 為空值時,系統預設使用name name |
index |
Int | 必 | 唯一索引 | 建議 1000+,新增取現有最大 +1 |
sortIndex |
Int | 必 | 排序索引 | 數值越小排序越前面 |
version |
Int | 必 | 規則版本 | 初始 1,更新規則時遞增 |
enabled |
Bool | 必 | 是否啟用 | 固定 true |
allowSearch |
Bool | 必 | 是否允許搜尋 | 不支援時設 false,並可省略 search 規則 |
allowDownload |
Bool | 必 | 是否允許下載 | 章節內容被拆成分頁,且從目錄 URL 無法得知內容頁數時設 false |
imageURL |
String | 否 | 列表圖示 | 站台 logo / favicon |
backgroundColor |
[Double] | 否 | 列表背景 | RGBA 陣列,如 [230,240,235,1];A 範圍 0 ~ 1 |
request:請求規則#
每個頁面請求都有兩塊:settings 管 URL / method / header,config
管編碼與載入方式。先確認要用 URLSession 或 WebView,再組 URL。
config — 載入與編碼
charEncodingRawValue
根據來源編碼進行調整requestMethodRawValue根據來源進行調整,1 可處理 Cloudflare,若需跨頁保留 cookie,再設定
websiteDataStoreRawValue: 1。
settings — URL 組裝
apiHost 不含結尾斜線;apiPath 以 /
開頭。組合順序:apiHost + apiPathPrefix + apiPath + 分頁前綴/索引 + apiPathSuffix + extraApiPathSuffix。
分頁常用
apiPageIndexPrefix、apiPathSuffix、apiPathReplacements;搜尋看
apiQueryTypeRawValue 決定 query 還是 path 模式。
settings 欄位 — 按用途分群
先依用途找到欄位群組,再對照範例填寫實際值。
apiHostString請求 host,不含結尾/apiPathString請求路徑,以/開頭httpMethodString僅支援GET/POST
apiPathPrefixString附加在 apiPath 之前apiPathSuffixString如.htmlapiPageIndexPrefixString分頁索引前綴,分類常用extraApiPathSuffixString關鍵字寫入路徑後再加固定段apiPathReplacementsMap第一頁與後續頁 URL 不同時用finalURLReplacementsMap組合後對最終 URL 做整段替換
apiQueryKeyString搜尋關鍵字鍵,系統會塞入關鍵字apiQueryParamsMap其餘固定 query 參數apiQueryTypeRawValueInt0 無 / 1 參數 / 2 路徑apiQueryPageIndexKeyString分頁帶 query 時用apiQueryLangRawValueInt0 繁體 / 1 簡體apiQueryValueFromPathMap從 path 抽出 query 值,分類常用
shouldIgnorePageIndexBool無分頁的分類設 trueignorePageIndexByKeywords[String]含特定關鍵字才忽略分頁
httpHeadersMap值為request_url代入完整連結timeoutDouble逾時秒數,預設 45delaySecondsDouble固定延遲,預設 0randomDelaySeconds[Double]隨機區間[min, max]needsURLEncodingBoolGET 已自動編碼,避免雙重needsCustomUserAgentBool使用客製 UA
jsScripts[JSScript]注入 JS 算 query。系統自動帶入keywords
config 欄位
charEncodingRawValueIntPayload 編碼:0 GBK / 1 BIG5 / 2 UTF-8(預設)requestMethodRawValueInt0 URLSession / 1 WebViewwebsiteDataStoreRawValueInt0 不持久化 / 1 持久化;cookie 跨頁時用 1
bodySelectorString等到此元素出現再解析mainFrameOnlyBooltrue 僅主頁面 / false 含 iframeinjectionTimeInt0 頁面開始前 / 1 DOM 建完
apiQueryTypeRawValue: 1 代表 GET query,2 代表把關鍵字寫入 path。Path
模式通常不設needsURLEncoding: true,避免雙重編碼。
response:解析規則#
將 HTML 或 JSON 轉成 App 可用資料。通常先用 listElement 找出多筆項目,如首頁、章節、搜尋等功能,詳情,再用各種
*Element 從每個項目內取值。
config
Response 編碼與 Cloudflare 驗證設定。
hostsToReplace
解析 path 時,系統會把結果中符合清單的 host 字串替換成空字串。
用途是讓絕對連結與相對連結輸出成一致的 path 格式。
只會處理列出的字串。
同站若同時有 https://、http://、www
或其他鏡像網域,需分別列入。
imageHost
封面解析到相對路徑時補圖片 host。多個圖片網域時須確認真實 CDN。
ParseElement 欄位#
所有 *Element 都是 ParseElement。必要欄位通常是 cssSelector 與
actionValue,其他欄位用於屬性提取、文字清理、前後綴補齊、JSON 包裝或特殊字串解析。
cssSelectorStringCSS 選擇器;定位元素actionValueInt解析類型,見 9 種對照
attributeKeyString屬性名稱(href/src/data-src)selectCssSelectorString先定位指定元素,多筆中取一筆selectIndexIntselectCssSelector 多結果取索引cssSelector2String同頁兩種結構備援selectCssSelector2String備援的指定元素selectIndex2Int備援的索引
prefixString取得後加前綴(拼 URL 用)suffixString取得後加後綴separatorString正文段落串接符號replacementsMapkey=結果文字 / value=要被替換的字blankReplacementsMap替換為空白;key"0"字面 /"1"正則rawContentReplacementsMap解析前對原始內容替換removeElements[String]解析前移除子元素(廣告 / script)
collectionSeparatorStringarray 合併符號collectionActionValueInt陣列內元素解析類型collectionValueIndexes[Int]取陣列指定索引
invalidTags[String]含此標籤則略過(actionValue: 22)replaceTags[String]標籤替換為換行(p / br 互轉)shouldNormaliseBool解析前補齊未閉合標籤shouldUnescapeUnicodeBool解\uXXXX→ 文字
parseableItems[Item]裁切非標準字串(actionValue: 20)isJSONFormatBool原始內容是 JSON,包成<ul><li>jsonKeyString只取 JSON 某 keyorderedKeys[String]JSON 鍵輸出順序,用nth-of-type對應
isJSONFormat — JSON 回應處理
當某環節(常見於 chapter、search 的 AJAX 介面)回傳 JSON 而非 HTML 時,於該環節的
listElement 設 isJSONFormat: true。框架會先把 JSON 遞迴展開成 HTML 列表 —— 物件/陣列的每個值包成
<li>,最外層套 <div id="json-content"> —— 之後即可照常用 cssSelector
解析。
jsonKey:只包裝指定鍵的內容。如 API 回{"status":0,"data":[…]},設"data"可略過外層status。orderedKeys:指定輸出欄位與順序;子元素再依此順序用nth-of-type(n)取值,未列出的鍵會被忽略。cssSelector一律從#json-content起算,列表通常為#json-content > ul > li。- 建議搭配
shouldUnescapeUnicode(還原\uXXXX)與shouldNormalise(補齊標籤)。 - JSON 只給 ID 而非完整路徑時,用
prefix/suffix拼回合法路徑。
JSON → HTML 轉換
// API 原始回應
{ "data": [ { "ordernum": "1", "title": "第一章" } ] }
// 框架自動轉換後(供 cssSelector 解析)
<div id="json-content">
<ul><li>
<ul><li>1</li><li>第一章</li></ul>
</li></ul>
</div>
response.chapter 配置
{
"chapter": {
"listElement": {
"isJSONFormat": true,
"jsonKey": "data",
"orderedKeys": ["ordernum", "title"],
"cssSelector": "#json-content > ul > li",
"shouldUnescapeUnicode": true,
"shouldNormalise": true
},
"pathElement": { "cssSelector": "ul li:nth-of-type(1)", "actionValue": 0,
"prefix": "p", "suffix": ".html" },
"nameElement": { "cssSelector": "ul li:nth-of-type(2)", "actionValue": 0 }
}
}
actionValue 對照#
actionValue 決定從元素內取得哪一種內容。常用值是 0 取文字、10
取屬性、2 多筆合併、22 解析段落正文。
| 值 | 名稱 | 用途 | 常見搭配 | 典型情境 |
|---|---|---|---|---|
0 |
text | 取元素文字 | replacements, blankReplacements |
書名、作者、章節名 |
2 |
array | 取多元素合併 | collectionSeparator, collectionValueIndexes,
collectionActionValue
|
多個分類標籤、多個作者 |
5 |
lastText | 取最後一個文字 | — | 同組連結中的「下一章」 |
6 |
lastAttr | 取最後一個屬性 | attributeKey |
同組連結中的「下一章」href |
9 |
firstText | 取第一個文字 | — | 同組連結中的「上一章」 |
10 |
firstAttr | 取第一個屬性 | attributeKey |
連結 href、圖片 src、meta content |
19 |
brTag | 用 br 切正文 | separator, removeElements |
傳統小說頁 <br> 換行 |
20 |
component | 裁切非標準字串 | parseableItems |
資料藏在混合字串中 |
22 |
pTag | 用 p 標籤切正文 | separator, invalidTags, replaceTags |
現代小說頁 <p> 段落 |
功能區塊對照#
這裡列出書源常見的 request / response 區塊。
Detail、Chapter、Content
通常是閱讀主流程。
Home、Category、Search 則依來源是否提供對應功能決定是否配置;若 inPage: true,代表資料在同一頁,可省略額外 request。
Home — 首頁推薦與分類
首頁通常解析推薦小說與分類標籤。request.home.inPage: true 代表分類也在首頁;false 則需補
home.category 請求。response.home 同時包含 main(推薦)與
category(分類)。
request.home
{
"home": {
"main": {
"settings": {
"apiHost": "https://example.com",
"apiPath": "/",
"httpMethod": "GET"
},
"config": { "charEncodingRawValue": 2 }
},
"inPage": true
}
}
response.home
{
"home": {
"template": 0,
"main": {
"listElement": { "cssSelector": ".book-list li" },
"nameElement": { "cssSelector": "a", "actionValue": 0 },
"pathElement": { "cssSelector": "a", "actionValue": 10, "attributeKey": "href" },
"imageElement": { "cssSelector": "img", "actionValue": 10, "attributeKey": "src" },
"pathRegex": "^/book/\\d+/?$"
},
"category": {
"listElement": { "cssSelector": "nav.cat a" },
"nameElement": { "cssSelector": "self", "actionValue": 0 },
"pathElement": { "cssSelector": "self", "actionValue": 10, "attributeKey": "href" }
}
}
}
Detail — 書籍資訊
解析封面、作者、簡介、分類、更新時間與最新章節名。若章節目錄不在詳細頁,需提供 chapterPathElement +
chapterPathRegex;多數站章節在同頁,省略即可。
request.detail
{
"detail": {
"main": {
"settings": { "apiHost": "https://example.com", "apiPath": "/", "httpMethod": "GET" },
"config": { "charEncodingRawValue": 2 }
},
"inPage": false
}
}
response.detail
{
"detail": {
"imageElement": { "cssSelector": ".cover img", "actionValue": 10, "attributeKey": "src" },
"authorElement": { "cssSelector": ".author", "actionValue": 0 },
"summaryElement":{ "cssSelector": ".intro", "actionValue": 0 },
"categoryElement":{ "cssSelector":".tag", "actionValue": 2, "collectionSeparator": ",",
"collectionActionValue": 0 },
"updatedTimeElement":{ "cssSelector": ".updated","actionValue": 0 },
"updatedTimeFormat": "yyyy-MM-dd"
}
}
Chapter — 章節目錄
核心欄位是 listElement、pathElement、nameElement。
完整目錄用
isPartial: false。
分頁目錄則需提供分頁請求與頁數解析方式,例如 pageCount 或
pageCountElement。AJAX 章節列表可用 isJSONFormat 處理。
request.chapter
{
"chapter": {
"main": {
"settings": { "apiHost": "https://example.com", "apiPath": "/", "httpMethod": "GET" },
"config": { "charEncodingRawValue": 2 }
},
"isPartial": false
}
}
response.chapter
{
"chapter": {
"listElement": { "cssSelector": ".chapter-list a" },
"nameElement": { "cssSelector": "self", "actionValue": 0 },
"pathElement": { "cssSelector": "self", "actionValue": 10, "attributeKey": "href" },
"reversed": false
}
}
Content — 章節正文
必填 chapterPathRegex、nameElement、contentElement、上一章與下一章路徑。相對檔名如
2.html 用 shouldReplaceBookPath 接回書籍路徑;複雜路徑用
isLastPathComponentReplaced + 前後綴重組。
request.content
{
"content": {
"settings": { "apiHost": "https://example.com", "apiPath": "/", "httpMethod": "GET" },
"config": { "charEncodingRawValue": 2 }
}
}
response.content (pTag 正文)
{
"content": {
"chapterPathRegex": "^/book/\\d+/\\d+\\.html$",
"nameElement": { "cssSelector": "h1.title", "actionValue": 0 },
"contentElement": {
"cssSelector": "div.txtnav",
"actionValue": 22,
"separator": "\n",
"removeElements": ["script", "style", ".ad"],
"invalidTags": ["<h", "<script"],
"replaceTags": ["<p>", "</p>", "<br>", "<br />"]
},
"lastChapterPathElement": { "cssSelector": ".prev", "actionValue": 10, "attributeKey": "href" },
"nextChapterPathElement": { "cssSelector": ".next", "actionValue": 10, "attributeKey": "href" }
}
}
Category — 分類列表
分類列表需要 pageSize 與列表解析欄位。
沒分頁時於 request 設
shouldIgnorePageIndex: true。
第一頁 URL 特殊時使用
apiPathReplacements。pageCount / pageCountElement 都不給時,系統不會依
pageSize 自動續抓,單次解析完成後即停止。
request.category
{
"category": {
"main": {
"settings": {
"apiHost": "https://example.com",
"apiPath": "/class_1_1.html",
"apiPathReplacements": { "_1.html": ".html" },
"apiPageIndexPrefix": "_",
"apiPathSuffix": ".html",
"httpMethod": "GET"
},
"config": { "charEncodingRawValue": 2 }
}
}
}
response.category
{
"category": {
"pageSize": 20,
"listElement": { "cssSelector": ".novel-list li" },
"nameElement": { "cssSelector": ".name", "actionValue": 0 },
"pathElement": { "cssSelector": "a", "actionValue": 10, "attributeKey": "href" },
"imageElement":{ "cssSelector": "img", "actionValue": 10, "attributeKey": "src" },
"authorElement":{ "cssSelector":".author","actionValue": 0 }
}
}
Search — 搜尋
多筆結果用 mPathElement / mNameElement;自動跳轉到單筆詳細頁時,補 sPathElement /
sNameElement 等 s-* 欄位,並用 cssSelector
指定該筆根節點。apiQueryTypeRawValue 決定 query / path 模式。
request.search (GET query)
{
"search": {
"settings": {
"apiHost": "https://example.com",
"apiPath": "/search.html",
"httpMethod": "GET",
"apiQueryKey": "q",
"apiQueryTypeRawValue": 1
},
"config": { "charEncodingRawValue": 2 }
}
}
response.search (多筆 + 單筆跳轉)
{
"search": {
"listElement": { "cssSelector": ".result-list li" },
"mNameElement": { "cssSelector": ".name", "actionValue": 0 },
"mPathElement": { "cssSelector": "a", "actionValue": 10, "attributeKey": "href" },
"cssSelector": "div.book",
"sNameElement": { "cssSelector": "h1", "actionValue": 0 },
"sPathElement": { "cssSelector": "link[rel=canonical]", "actionValue": 10,
"attributeKey": "href" }
}
}
常見場景配置#
以下整理範例中常見的配置方式。遇到相同情境時,可先採用相近結構,再依目標站台調整 selector、regex、header 與編碼。
Cloudflare / WebView 驗證
頁面需要瀏覽器環境或挑戰驗證時,在 request config 改用 WebView。
response config
只放用來判斷挑戰與驗證完成的關鍵字。shouldReload 視站台驗證後是否需要重新載入頁面而定,不是固定必填。
// request.config
{
"requestMethodRawValue": 1,
"websiteDataStoreRawValue": 1
}
// response.config
{
"charEncodingRawValue": 2,
"challengeKeywords": ["challenge"],
"challengeVerifiedKeywords": ["cf_clearance"],
"challengeCookieDomains": [".example.com"],
"authStorageTypeRawValue": 0,
"waitForCookie": true
}
AJAX 章節列表(JSON API)
詳細頁可從 meta 或 DOM 取 book id,用 prefix / suffix 拼出章節 API。回 JSON 時用
isJSONFormat+jsonKey+orderedKeys,後續仍用 CSS selector 解析。
{
"listElement": {
"isJSONFormat": true,
"jsonKey": "data",
"orderedKeys": ["id", "title"],
"cssSelector": "#json-content > ul > li"
},
"pathElement": {
"cssSelector": "ul > li:nth-of-type(1)",
"actionValue": 0,
"prefix": "/book/40459/",
"suffix": ".html"
},
"nameElement": { "cssSelector": "ul > li:nth-of-type(2)", "actionValue": 0 }
}
pTag 正文清理
actionValue: 22 適合段落以 <p> 呈現的頁面。先 removeElements
移除標題、廣告、script,再用 invalidTags + replaceTags 控制輸出。
{
"contentElement": {
"cssSelector": "div.txtnav",
"actionValue": 22,
"separator": "\n",
"removeElements": ["script", "style", ".bottem", ".ad"],
"invalidTags": ["<h", "<script", "</script", "⊥"],
"replaceTags": ["<p>", "</p>", "<br>", "<br />"]
}
}
brTag 正文(傳統小說頁)
頁面用 <br> 換行時用 actionValue: 19。記得移除站台廣告與固定提示文字。
{
"contentElement": {
"cssSelector": "#content",
"actionValue": 19,
"separator": "\n",
"removeElements": ["script", ".adsbygoogle"],
"blankReplacements": {
"0": ["請記住本站永久域名", "天才一秒記住"]
}
}
}
路徑正規化 + 章節接書路徑
解析結果若包含站台 host,可用 hostsToReplace 移除指定 host 字串,輸出一致的 path 格式。
正文若回相對檔名(如
2.html),用 shouldReplaceBookPath + bookPathReplacements 接回。
{
"hostsToReplace": [
"https://www.example.com",
"http://www.example.com",
"https://example.com"
],
"content": {
"chapterPathRegex": "^/book/\\d+/\\d+\\.html$",
"shouldReplaceBookPath": true,
"bookPathReplacements": { "/": [".html"] }
}
}
多分類標籤(actionValue: 2)
多個 label 元素合併成一個字串,並可只取特定索引(如第一個分類)。
{
"authorElement": {
"actionValue": 2,
"cssSelector": ".labelbox label",
"collectionSeparator": ", ",
"collectionActionValue": 0,
"collectionValueIndexes": [0]
}
}
JS 動態簽名(搜尋加密)
搜尋需 JS 加密簽名時,注入 source 函式並用 invoke 呼叫,結果以 key
為鍵存入查詢字典。keywords 是系統自動帶入的關鍵字。
{
"jsScripts": [{
"key": "sig",
"source": "function encode(s){ return btoa(unescape(encodeURIComponent(s))); }",
"invoke": "encode(keywords)"
}]
}
分類無分頁 / 條件忽略分頁
分類沒分頁時忽略索引;只在含特定關鍵字(如 author)時才忽略。
{
"shouldIgnorePageIndex": true,
"ignorePageIndexByKeywords": ["author", "tag"]
}
範例書源與 Agent 文檔#
此區列出 18 份完整書源範例與 1 份 AI Agent 技能文檔。新增書源時,建議先找一份與目標站台結構接近的範例,對照其
request、response、selector 與 regex 設定。範例檔案位於 examples/。
點擊任一張卡片可預覽內容,並在預覽視窗內複製或下載。
52書庫、全本小說;WebView 可參考 思兔閱讀;AJAX 章節列表可參考
吞噬小说;POST 搜尋可參考 zhaoshuyuan;分頁目錄可參考 明智屋小說。
最小可用模板#
下方保留必要骨架。建立新書源時,可先替換基本資訊,再依站台補上 selector、regex、編碼、header 與分頁設定。
{
"info": {
"type": 99,
"name": "書源名稱",
"alias": "書源名稱",
"index": 1105,
"version": 1,
"enabled": true,
"sortIndex": 1105,
"allowSearch": true,
"allowDownload": true
},
"data": {
"searchIndex": 1105,
"request": {
"home": { "main": { "settings": {}, "config": {} }, "inPage": true },
"detail": { "main": { "settings": {}, "config": {} }, "inPage": false },
"chapter": { "main": { "settings": {}, "config": {} }, "isPartial": false },
"content": { "settings": {}, "config": {} },
"category": { "main": { "settings": {}, "config": {} } },
"search": { "settings": {}, "config": {} }
},
"response": {
"config": { "charEncodingRawValue": 2 },
"hostsToReplace": ["https://example.com"],
"imageHost": "https://example.com",
"home": {},
"detail": {},
"chapter": {},
"content": {},
"category": {},
"search": {}
}
}
}
疑難排解#
依常見症狀整理可能原因與處理方式。遇到解析或請求異常時,先從對應條目檢查。
列表全部解析成同一筆,或只有第一筆
listElement 沒指到「重複項目」,而是指到整個容器。檢查 selector 是否落在每筆共用的父節點上,例如
.book-list li,不要只選 .book-list。
{
"listElement": { "cssSelector": ".book-list li" },
"nameElement": { "cssSelector": "a", "actionValue": 0 },
"pathElement": { "cssSelector": "a", "actionValue": 10, "attributeKey": "href" }
}
正文混入廣告、站台提示或 script
removeElements 移除子元素,再用 blankReplacements 清理固定字串。使用
actionValue: 22 時,可搭配 invalidTags 與 replaceTags 統一段落格式。
{
"contentElement": {
"cssSelector": "#content",
"actionValue": 22,
"separator": "\n",
"removeElements": ["script", "style", ".ad"],
"blankReplacements": {
"1": ["請記住本站.*", "本章未完.*"]
},
"invalidTags": ["<script", "<style"],
"replaceTags": ["<p>", "</p>", "<br>", "<br />"]
}
}
章節目錄需要分頁載入
pageCountElement 或固定
pageCount。若 API 回傳 JSON,可先包裝成可用 CSS selector 解析的結構。
{
"request": {
"chapter": {
"main": {
"settings": {
"apiHost": "https://example.com",
"apiPath": "/book/123/",
"apiPageIndexPrefix": "page_",
"apiPathSuffix": ".html",
"httpMethod": "GET"
},
"config": { "charEncodingRawValue": 2 }
},
"isPartial": true
}
},
"response": {
"chapter": {
"pageCount": 5,
"listElement": { "cssSelector": ".chapter-list a" },
"nameElement": { "cssSelector": "self", "actionValue": 0 },
"pathElement": {
"cssSelector": "self",
"actionValue": 10,
"attributeKey": "href"
}
}
}
}
Cloudflare 驗證後仍無法取得內容
需要時,request 使用
requestMethodRawValue: 1。response config 放挑戰判斷與驗證完成關鍵字;
shouldReload
只在驗證完成後需要重新載入頁面時才設定。
{
"request": {
"detail": {
"main": {
"config": {
"requestMethodRawValue": 1,
"websiteDataStoreRawValue": 1
}
}
}
},
"response": {
"config": {
"challengeKeywords": ["challenges.cloudflare", "Just a moment"],
"challengeVerifiedKeywords": ["cf_clearance"],
"challengeCookieDomains": [".example.com"],
"authStorageTypeRawValue": 0,
"waitForCookie": true
}
}
}
搜尋自動跳轉到單本詳細頁,結果空白
cssSelector 指定該筆容器,讓後續解析與多筆流程一致。
{
"search": {
"cssSelector": "div.book",
"sNameElement": { "cssSelector": "h1", "actionValue": 0 },
"sPathElement": {
"cssSelector": "link[rel=canonical]",
"actionValue": 10,
"attributeKey": "href"
}
}
}
章節順序顛倒(最新章節在最前面)
response.chapter 設
reversed: true。partialReversed 只用於分頁章節目錄,表示每一頁內的章節也需要反轉。若要依章節路徑或章節名稱排序,再搭配
sortTypeRawValue。
{
"chapter": {
"reversed": true,
"sortTypeRawValue": 1
}
}
HTML 結構不完整 / 只有關閉標籤
shouldNormalise: true,系統會先補齊再解析。若資料是非標準字串,改用
actionValue: 20 + parseableItems 裁切。
{
"nameElement": {
"cssSelector": "script",
"actionValue": 20,
"parseableItems": [
{ "value": "bookName: '", "direction": 2 },
{ "value": "'", "direction": 1 }
]
}
}
分類第一頁與第二頁 URL 不同
apiPathReplacements 把第一頁特殊段轉成第二頁起的模式,再讓系統套上
apiPageIndexPrefix + 索引。若需要等完整 URL 組合後再改,使用
finalURLReplacements。
{
"settings": {
"apiPathReplacements": {
"_1.html": ""
},
"apiPageIndexPrefix": "_",
"apiPathSuffix": ".html",
"finalURLReplacements": {
"/index.html": "/"
}
}
}
驗收清單#
完成規則後,逐項確認結構、路徑與解析結果。
先實測詳細、章節與正文;首頁、分類與搜尋依來源支援情況測試。
規則完整性
info.index與既有書源不衝突(建議 1000+)- 已覆蓋來源支援的功能區塊;不支援分類或搜尋時可省略
hostsToReplace、pathRegex、chapterPathRegex對齊最終路徑格式- 若支援分類或搜尋,需實測分類分頁與搜尋多筆結果
- JSON 結構合法,可被 App 正常匯入
解析品質
listElement直接指向重複項目,避免整個容器解析成單筆- 正文已移除廣告、script、導覽按鈕與站台提示文字
- 若支援搜尋,需處理多筆結果;若站台會單筆跳轉,再補 s-* 欄位
- Cloudflare / AJAX / GBK / 動態參數,request 與 response 對齊
- 封面 URL 完整可載入(必要時加
imageHost)
實測順序建議
- 把書源 JSON 匯入 App,確認列表可見且能進入
- 若有首頁推薦,確認列表可正常載入與點擊
- 若有分類列表,至少測試第二頁或下一頁載入
- 若有搜尋,測試多筆關鍵字;若站台會單筆跳轉,再測 s-* 欄位
- 進入章節目錄、開啟正文,驗證上一章 / 下一章按鈕
- 進入正文頁查看內容是否完整