Legado 书源规则说明

原作者: Celeter
现作者: 喵公子
更新时间: 2024-02-27

描述: 本说明是 legado 的规则说明,禁止其他未获得本人授权的第三方软件使用


概况

  1. 语法说明
  2. Legado 的特殊规则
  3. 书源之「基本」
  4. 书源之「搜索」
  5. 书源之「发现」
  6. 书源之「详情页」
  7. 书源之「目录」
  8. 书源之「正文」
  9. 补充说明

1、语法说明

JSOUP 之 Default

  • 语法如下:

  • @ 为分隔符,用来分隔获取规则

    • 每段规则可分为 3 段
    • 第一段是类型,class、id、tag、text、children 等,children 获取所有子标签,不需要第二段和第三段,text 可以根据文本内容获取
    • 第二段是名称,text。第二段为文本内容的一部分
    • 第三段是位置,class、tag、id 等会获取到多个,所以根据位置
    • 不加位置会获取所有
    • 位置正着数从 0 开始,0 是第一个,为负数则是取倒着数的值,-1 为倒数第一个,-2 为倒数第二个
    • ! 是排除,有些位置不符合需要排除用 ! 后面的序号用 : 隔开,0 是第 1 个,负数为倒数序号,-1 最后一个,-2 倒数第 2 个,依次
    • 获取列表的最前面加上负号。可以使用表倒置,有些网站目录列表是倒的,前面加个负号可变为正的
    • @ 的最后一段为获取内容,text、textNodes、ownText、href、src、html、all 等
    • 需要正则替换在最后加上 ##正则表达式##替换内容,替换内容为空时,第二个 ## 可以省略
    • 例: class.add.0@tag.a.0@text||tag.dd.0@tag.h1@text##全文阅读
    • 例: class.add.0@tag.a.0@text&&tag.dd.0@tag.h1@text##全文阅读
  • 增加支持类似数组的方法

    • 格式:[index,index,...][[index,index,...],其中 [! 开头表示筛选方式为排除,index 可以是单个索引,也可以是区间
    • 区间格式为 [start:end][start:end:step],其中 start 为 0 时可省略,end 为 -1 时可省略
    • 索引(index)、区间两端(start 和 end)、区间间隔(step)都支持负数
    • 特殊用法 tag.div[-1:0],可在任意地方让列表反向
    • 允许索引作为分段后每个部分的首规则,此时相当于前面是 children
    • head@.{@texthead@[1]@texthead@children[1]@text 等价

JSOUP 之 CSS

注意:获取内容可用 text、textNodes、ownText、html、all、href、src 等
例子见最后的【书源一】的搜索页和正文页规则

JSONPath

  • 语法见 JsonPath 教程
  • 最好以 @json:$. 开头,其他形式不可靠
  • 标准规范 goessner JSONPath - XPath for JSON
  • 实现库 jsonpath/JsonPath
  • 在线测试 Jayway JsonPath Evaluator

例子见最后的【书源二】的搜索页、目录页和正文页规则

XPath

  • 语法见 XPath 教程-入门、XPath 教程-基础、XPath 教程-高级、XPath 库的说明
  • 必须以 @XPath:// 开头
  • 标准规范 W3C XPATH 1.0
  • 实现库 zhegexiaohuozi/JsoupXpath

例子见最后的【书源二】的搜索页、详情页和正文页规则,以及目录页的下一页规则

JavaScript

  • 可以在 <js></js>@js: 中使用,结果存在 result 中
  • @js: 只能放在其他规则的最后使用
  • <js></js> 可以在任意位置使用,还能作为其他规则的分隔符,例如:tag.li<js></js>/a
  • 在搜索列表、发现列表和目录中使用可以用 + 开头,使用 AllInOne 规则

正则之 AllInOne

  • 只能在搜索列表、发现列表、详情页预加载和目录列表中使用
  • 必须以 : 开头
  • 教程 Regex 专题(E404)
  • 语法(E404)、方法(E404)、引壁(E404)
  • 例子见最后的【书源一】的目录页规则,最前面的 - 表示目录倒序,以及【书源二】的目录页规则

正则之 OnlyOne

  • 形式 ##正则表达式#替换内容##
  • 只能在搜索列表、发现列表、详情页预加载、目录列表之外使用
  • 例子见最后的【书源一】的详情页规则

注意点:该规则只能获取第一个匹配到的结果并进行替换

正则之净化

  • 形式 ##正则表达式#替换内容
  • 只能在其他规则后面,独立使用相当于 all##正则表达式##替换内容
  • 例子见最后的【书源一】的正文页规则

注意点:该规则为循环匹配替换

自定义三种连接符号

  • 符号:&&||%%
  • 只能在同种规则间使用,不包括 js 和正则
  • && 会合并所有取到的值
  • || 会以第一个取到值的为准
  • %% 会依次取数,三个列表,先取列表 1 的第一个,再取列表 2 的第一个,再取列表 3 的第一个,再取列表 1 的第二个,再取列表 2 的第二个...

2、Legado 的特殊规则

URL 必知必会

1. 请求头

  • 一般形式,如下所示:
json
{
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36",
    "Accept-Language": "zh-CN,zh;q=0.9"
}
  • 复杂情况可使用 js:
javascript
(() => {
    var ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36";
    var headers = { "User-Agent": ua };
    return JSON.stringify(headers);
})()

其中,ua 必须保证是 JavaScript 的 String 类型,JSON.stringify() 才能将 header 转换为字符串。

  • 获取登录后的 cookie:
javascript
java.getCookie("http://baidu.com", null) // => userId=1234;pwd=adbcd
java.getCookie("http://baidu.com", "userid") // => 1234
  • 请求头中支持 http 代理、socks4、socks5 代理设置:
json
// socks5 代理
{
    "proxy": "socks5://127.0.0.1:1080"
}
// http 代理
{
    "proxy": "http://127.0.0.1:1080"
}
// 支持代理服务器验证
{
    "proxy": "socks5://127.0.0.1:1080使用户名@密码"
}
// 注意:这些请求头是无意义的,会被忽略掉

2. GET 请求

  • 一般形式下,charset 为 utf-8 时可省略,无特殊情况不需要请求头和 webView,参数 webView 非空时采用 webView 加载:
javascript
https://www.baidu.com,
({
    "charset": "gbk",
    "headers": {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
    },
    "webView": true
})
  • 复杂情况可使用 js:
javascript
var ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36";
var headers = { "User-Agent": ua };
var option = {
    "charset": "gbk",
    "headers": headers,
    "webView": true
};
"https://www.baidu.com," + JSON.stringify(option)

3. POST 请求

  • 一般形式下,body 是请求体,charset 为 utf-8 时可省略,无特殊情况不需要请求头和 webView,参数 webView 非空时采用 webView 加载:
javascript
https://www.baidu.com,
({
    "charset": "gbk",
    "method": "POST",
    "body": "bid=10086",
    "headers": {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
    },
    "webView": true
})
  • 复杂情况可使用 js:
javascript
var ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36";
var headers = { "User-Agent": ua };
var body = "bid=" + "10086";
var option = {
    "charset": "gbk",
    "method": "POST",
    "body": String(body),
    "headers": headers,
    "webView": true
};
"https://www.baidu.com," + JSON.stringify(option)

其中,body 必须保证是 JavaScript 的 String 类型,变量是计算得到的尽量都用 String() 强转一下类型。

变量的 put 与 get

  • @put@get
    只能用于 js 以外的规则中,@put 里使用 JSONPath 不需要加引号,其他规则需要加引号,
    例如:@put:{bid://*|@bid-data|/@bid-data}

  • java.putjava.get
    只能用于 js 中,在 js 中无法使用 @get

{()}() 规则

  • 在搜索 URL 与发现 URL 中的 (1)
    () 里只能使用 js

  • 在搜索 URL 与发现 URL 以外的 (1)
    可在 (1) 中使用任意规则(正则除外?),默认为 js,使用其他规则需要有明显的标志头,
    Default 规则需要以 @@ 开头,XPath 需要以 @xpath:// 开头,JSONPath 需要以 @json:$. 开头,CSS 需要以 @css: 开头

自定义 js

  • 在 js 中调用 java 的常规方法:由于 java 这个关键字已经被使用,调用 java 开头的包名时需使用全局变量 Packages 参考脚本之家
    只调用某个 public 函数:例:io.legado.app.utils.htmlFormat(str)org.jsonp.Jsoup.parse(str)
    直接引入 java 类,下所示,引入了两个 java 包,java 包的作用域是在 with 的范围内,其内使用 java 相关语法,最后在使用域外被 j 调用了作用域内的函数:
javascript
var javaImport = new JavaImporter();
javaImport.importPackage(
    Packages.java.lang,
    Packages.java.security
);
with (javaImport) {
    function strToMd5By32(str) {
        var reStr = null;
        var md5 = MessageDigest.getInstance("MD5");
        var bytes = md5.digest(String(str).getBytes());
        var stringBuffer = new StringBuilder();
        bytes.forEach(a => {
            var bt = a & 0xff;
            if (bt < 16) {
                stringBuffer.append("0");
            }
            stringBuffer.append(Integer.toHexString(bt));
        });
        reStr = stringBuffer.toString();
        return reStr;
    }
}

变量

  • baseUrl // 变量-当前 url, String
  • result // 变量-上一步的结果
  • book // 变量-书籍类, 方法见 io.legado.app.data.entityss.Book
  • cookie // 变量-cookie 操作类, 方法见 io.legado.app.help.http.CookieStore
  • cache // 变量-缓存操作类, 方法见 io.legado.app.help.CacheManager
  • chapter // 变量-当前目录类, 方法见 io.legado.app.data.entityss.BookChapter
  • title // 变量-当前标题, String
  • src // 内容, 源码

常用函数

下面是一些常用的函数,详见 JsExtensions.kt

javascript
// 访问网络,urlStr 为 url 字符串,返回类型 String?
java.ajax(urlStr: String)

// 并发访问网络,urlList 为 url 数组,返回 StrResponse? 的数组,若要获取 body,需使用 .body()
java.ajaxAll(urlList: Array<String>): Array<String>>

// 访问网络,urlStr 为 url 字符串,返回 Response<String>,已废弃
java.connect(urlStr: String)

// 文件下载,content 为十六进制字符串,url 用于生成文件名,返回 String 文件相对路径
java.downloadFile(content: String, url: String)

// 实现重定向拦截,返回 Connection.Response
java.get(url: String, headers: Map<String, String>)
java.post(urlStr: String, body: String, headers: Map<String, String>)

// 实现 cookie 读取,返回 String
java.getCookie(tag: String, key: String?)

// base64 解码,返回类型 String
java.base64Decode(str: String)
java.base64Decode(str: String, flags: Int)

// base64 解码,返回类型 ByteArray?
java.base64DecodeToByteArray(str: String?)
java.base64DecodeToByteArray(str: String?, flags: Int)

// base64 编码,返回类型 String?
java.base64Encode(str: String)
java.base64Encode(str: String, flags: Int)

// md5 编码,返回类型 String?
java.md5Encode(str: String)
java.md5Encode16(str: String)

// 格式化时间戳,返回类型 String
java.timeFormat(timestamp: Long)
java.timeFormat(time: String)

// utf8 编码转成 gbk 编码,返回 String
java.utf8ToGbk(str: String)

// 实现字符串的 URI 编码,enc 为编码格式,返回 String
java.encodeURI(str: String) // 默认 enc="UTF-8"
java.encodeURI(str: String, enc: String)

// html 格式化,返回 String
java.htmlFormat(str: String)

// 获取本地文件,path 为文件的相对路径,返回 File
java.getFile(path: String)

// 读取本地文件,返回 ByteArray?
java.readFile(path: String)

// 读取本地文本文件,charsetName 为编码格式
java.readTxtFile(path: String) // 自动识别 charsetName 不一定准,乱码时请手动指定
java.readTxtFile(path: String, charsetName: String)

// 删除文件或文件夹
deleteFile(path: String)

// zip 文件解压,zipPath 为压缩文件路径,返回 String 解压相对路径,会删除原文件只保留解压后的文件
java.unzipFile(zipPath: String)

// 文件夹内所有文本文件读取,返回内容 String,会删除文件夹
java.getTxtInfoLder(unzipPath: String)

// 获取网络 zip 文件中的数据,url 为 zip 文件链接,path 为所需获取文件在 zip 内的路径,返回文件数据 String
java.getZipStringContent(url: String, path: String)

// 获取网络 zip 文件中的数据,url 为 zip 文件链接,path 为所需获取文件在 zip 内的路径,返回文件数据 ByteArray?
java.getZipByteArrayContent(url: String, path: String)

// 解析字体,返回字体解析类 QueryTTF?
java.queryBase64TTF(base64: String)

// str 支持 url、本地文件、base64,自动判断,自动缓存,返回字体解析类 QueryTTF?
java.queryTTF(str: String?)

// text 为包含错误字体的内容,font1 为错误的字体,font2 为正确的字体,返回字体对应的字
java.replaceFont(text: String, font1: QueryTTF?, font2: QueryTTF?)

// 输出调试日志
java.log(msg: String)

// AES 解码为 ByteArray?,str 为传入的 AES 加密数据,key 为 AES 解密 key,transformation 为 AES 加密方式,iv 为 ECB 模式的偏移向量
java.aesDecodeToByteArray(str: String, key: String, transformation: String, iv: String)

// AES 解码为 String?,str 为传入的 AES 加密数据,key 为 AES 解密 key,transformation 为 AES 加密方式,iv 为 ECB 模式的偏移向量
java.aesDecodeToString(str: String, key: String, transformation: String, iv: String)

// 已经 base64 的 AES 解码为 ByteArray?,str 为 Base64 编码数据,key 为 AES 解密 key,transformation 为 AES 加密方式,iv 为 ECB 模式的偏移向量
java.aesBase64DecodeToByteArray(str: String, key: String, transformation: String, iv: String)

// 已经 base64 的 AES 解码为 String?,str 为 Base64 编码数据,key 为 AES 解密 key,transformation 为 AES 加密方式,iv 为 ECB 模式的偏移向量
java.aesBase64DecodeToString(str: String, key: String, transformation: String, iv: String)

// 加密 aes 为 ByteArray?,data 为传入的原始数据,key 为 AES 解密 key,transformation 为 AES 加密方式,iv 为 ECB 模式的偏移向量
java.aesEncodeToByteArray(data: String, key: String, transformation: String, iv: String)

// 加密 aes 为 String?,data 为传入的原始数据,key 为 AES 解密 key,transformation 为 AES 加密方式,iv 为 ECB 模式的偏移向量
java.aesEncodeToString(data: String, key: String, transformation: String, iv: String)

// 加密 aes 后 Base64 化的 ByteArray?,data 为传入的原始数据,key 为 AES 解密 key,transformation 为 AES 加密方式,iv 为 ECB 模式的偏移向量
java.aesEncodeToBase64ByteArray(data: String, key: String, transformation: String, iv: String)

// 加密 aes 后 Base64 化的 String?,data 为传入的原始数据,key 为 AES 解密 key,transformation 为 AES 加密方式,iv 为 ECB 模式的偏移向量
java.aesEncodeToBase64String(data: String, key: String, transformation: String, iv: String)

URL 参数

  • url 添加 js 参数,解析 url 时执行,可在访问 url 时处理 url,例:
javascript
https://www.baidu.com,'"js":'java.headerMap.put('xxx', 'yyy')")
https://www.baidu.com,'"js":'java.url=java.url+'yyyy'")
  • url 全部参数,详见 AnalyzeUrl.txt
kotlin
data class UrlOption(
    val method: String?,
    val charset: String?,
    val webView: Any?,
    val headers: Any?,
    val body: Any?,
    val type: String?,
    val js: String?,
    val retry: Int = 0 // 重试次数
)

3、书源之「基本」

  • 书源 URL (bookSourceUrl)
    必填,唯一标识,不可重复,与其他源相同会覆盖

  • 书源名称 (bookSourceName)
    必填,名字可重复

  • 书源分组 (bookSourceGroup)
    可不填,用于整理源

  • 登录 URL (loginUrl)
    根据需求,随机应变,用于登录个人账户

  • 书籍 URL 正则 (bookUrlPattern)
    可不填,添加网址时,用于识别书源
    例: https?://www.piaotian.com/bookinfo/.*

  • 请求头 (header)
    根据需求,随机应变,访问网址时使用


4、书源之「搜索」

  • 搜索地址 (url)
    key 为关键字标识,通常形态为 {{key}},运行时会替换为搜索关键字
    也可以对 key 进行加密等操作,例如:{{java.base64Encode(key)}}
    page 为关键字标识,通常形态为 {{page}},page 的初值为 1,也可以对 page 进行计算,例如:{{page-1*20}}
    有时会遇到第一页没有页数的情况,有两种方法:
    {{page - 1 == 0 ? "" : page}}
    <,{{page}}>
    支持相对 URL

  • 书籍列表规则 (bookList)

  • 书名规则 (name)

  • 作者规则 (author)

  • 分类规则 (kind)

  • 字数规则 (wordCount)

  • 最新章节规则 (lastChapter)

  • 简介规则 (intro)

  • 封面规则 (coverUrl)

  • 详情页 url 规则 (bookUrl)


5、书源之「发现」

  • 发现地址规则 (url)
    page 为关键字标识,通常形态为 {{page}},page 的初值为 1,也可以对 page 进行计算,例如:{{page-1*20}}
    有时会遇到第一页没有页数的情况,有两种方法:
    {{page - 1 == 0 ? "" : page}}
    <,{{page}}>
    格式一,名称:http://www.baidu.com,发现 URL 可使用 && 或换行符 \n 隔开
    格式二,有 5 个样式属性(layout_flexGrow、layout_flexShrink、layout_alignSelf、layout_flexBasisPercent、layout_wrapBefore)需要了解,详情见简书,写法:
json
{
    "title": "今日限免",
    "url": "https://app-cdn.jjwxc.net/bookstore/getFullPage?channel=novelfree",
    "style": {
        "layout_flexGrow": 1
    }
},
{
    "title": "频道金榜",
    "url": "http://app-cdn.jjwxc.net/bookstore/getFullPage?channelBody=%7B%229%22%3A%7B%22offset%22%3A%22<,{{(page-1)*25}}%22%2C%221imit%22%3A%2225%22%70%7D&versionCode=148",
    "style": {
        "layout_flexGrow": 0,
        "layout_flexShrink": 1,
        "layout_alignSelf": "auto",
        "layout_flexBasisPercent": -1,
        "layout_wrapBefore": true
    }
},
{
    "title": "幻想未来",
    "url": "http://app-cdn.jjwxc.net/bookstore/getFullPage?channelBody=%7B%2222000023%22%3A%7B%22offset%22%3A%22<,{{(page-1)*25}}%22%2C%221imit%22%3A%2225%22%70%7D&versionCode=148"
}

支持相对 URL

  • 书籍列表规则 (bookList)
  • 书名规则 (name)
  • 作者规则 (author)
  • 分类规则 (kind)
  • 字数规则 (wordCount)
  • 最新章节规则 (lastChapter)
  • 简介规则 (intro)
  • 封面规则 (coverUrl)
  • 详情页 url 规则 (bookUrl)

6、书源之「详情页」

  • 预处理规则 (bookInfoInit)
    只能使用正则之 AllInOne 或者 js
    正则之 AllInOne 必须以 : 开头
    js 的返回值需要是 json 对象,例:
javascript
(function () {
    return {
        a: "圣墟",
        b: "辰东",
        c: "玄幻",
        d: "200万字",
        e: "第两千章 辰东宫殿",
        f: "在破败中崛起,在寂灭中复苏。沧海成尘,雷电枯竭...",
        g: "https://bookcover.yuewen.com/qdbimg/349573/1004608738/300",
        h: "https://m.qidian.com/book/1004608738"
    };
})()

此时,书名规则填 a,作者规则填 b,分类规则填 c,字数规则填 d,最新章节规则填 e,简介规则填 f,封面规则填 g,目录 URL 规则填 h

  • 书名规则 (name)
  • 作者规则 (author)
  • 分类规则 (kind)
  • 字数规则 (wordCount)
  • 最新章节规则 (lastChapter)
  • 简介规则 (intro)
  • 封面规则 (coverUrl)
  • 目录 URL 规则 (tocUrl)
  • 只支持单个 url
  • 允许修改书名作者 (canReName)
    • 规则不为空且详情页书名不为空,使用详情页中的作者。否则,使用搜索页中的书名
    • 规则不为空且详情页作者不为空,使用详情页中的作者。否则,使用搜索页中的作者

7、书源之「目录」

  • 目录列表规则 (chapterList)
    首字符使用负号(一)可使列表反序
  • 章节名称规则 (ruleChapterName)
  • 章节 URL 规则 (chapterUrl)
  • VIP 标识 (isVip)
    当结果为 null、false、0、"" 时为非 VIP
  • 章节信息 (ChapterInfo)
    可调用 java.timeFormat(timestamp: Long) 将时间戳转为 yyyy/MM/dd HH:mm 格式的时间
  • 目录下一页规则 (nextToCUrl)
    支持单个 url,支持 url 数组
    js 中返回 []null"" 时停止加载下一页

8、书源之「正文」

  • 正文规则 (content)
    正文图片链接支持修改 headers:
javascript
let options = {
    "headers": {
        "User-Agent": "xxxx",
        "Referrer": baseUrl,
        "Cookie": "aaa=bbb;"
    }
};
'<img src="' + src + ', ' + JSON.stringify(options) + '>'

book 对象的可用属性

使用方法:在 js 中或 {()} 中使用 book.属性 的方式即可获取,在正文内容后加上 ###{book.name+"正文卷"+title} 可以净化书名+正文卷+章节名称(例如:我是大明星正文卷第二章我爸是豪门总裁)这一类的字符。

  • bookUrl // 详情页 Url(本地书源存储完整文件路径)
  • tocUrl // 目录页 Url(toe-table of Contents)
  • origin // 书源 URL(默认 BookType.local)
  • originName // 书源名称 or 本地书籍文件名
  • name // 书籍名称(书源获取)
  • author // 作者名称(书源获取)
  • kind // 分类信息(书源获取)
  • customTag // 分类信息(用户修改)
  • coverUrl // 封面 Url(书源获取)
  • customCoverUrl // 封面 Url(用户修改)
  • intro // 简介内容(书源获取)
  • customIntro // 简介内容(用户修改)
  • charset // 自定义字符串名称(仅适用于本地书籍)
  • type // 0:text 1:audio
  • group // 自定义分组索引号
  • latestChapterTitle // 最新章节标题
  • latestChapterTime // 最新章节标题更新时间
  • lastCheckTime // 最近一次更新书籍信息的时间
  • lastCheckCount // 最近一次发现新章节的数量
  • totalChapterNum // 书籍目录总数
  • durChapterTitle // 当前章节名称
  • durChapterIndex // 当前章节索引
  • durChapterPos // 当前阅读的进度(首行字符的索引位置)
  • durChapterTime // 最近一次阅读书籍的时间(打开正文的时间)
  • canUpdate // 刷新书架时更新书籍信息
  • order // 手动排序
  • originOrder // 书源排序
  • variable // 自定义书籍变量信息(用于书源规则检索书籍信息)

chapter 对象的可用属性

使用方法:在 js 中或 {()} 中使用 chapter.属性 的方式即可获取,在正文内容后加上 ##{{chapter.title+chapter.index}} 可以净化章节标题+序号(例如:第二章 天仙下凡2)这一类的字符。

  • url // 章节地址
  • title // 章节标题
  • baseUrl // 用来拼接相对 url
  • bookUrl // 书籍地址
  • index // 章节序号
  • resourceUrl // 音频真实 URL
  • tag // 章节信息
  • start // 章节起始位置
  • end // 章节终止位置
  • variable // 变量

示例

下示例,在详情页(目录页)和正文使用 webView 加载,例:

json
{
    "bookSourceComment": "",
    "bookSourceGroup": " 有声",
    "bookSourceName": "猫耳FM",
    "bookSourceType": 1,
    "bookSourceUrl": "https://www.missevan.com",
    "customOrder": 0,
    "enabled": false,
    "enabledExplore": true,
    "lastUpdateTime": 0,
    "ruleBookInfo": {},
    "ruleContent": {
        "content": "https://static.missevan.com/{//*[contains(@class,\"pld-sound-active\")]}/@data-soundurl64}",
        "sourceRegex": "",
        "webls": ""
    },
    "ruleExplore": {},
    "ruleSearch": {
        "author": "author",
        "bookList": "$.info.Datas",
        "bookUrl": "https://www.missevan.com/mdrama/drama/{{$.id}},{\"webView\":true}",
        "coverUrl": "cover",
        "intro": "abstract",
        "kind": "{{$.type_name}},{{$.catalog_name}}",
        "lastChapter": "newest",
        "name": "name",
        "wordCount": "catalog_name"
    },
    "ruleToc": {
        "chapterList": "@css:.scroll-list.btn-groups>a",
        "chapterName": "text",
        "chapterUrl": "href##$##,{\"webView\":true}"
    },
    "searchUrl": "https://www.missevan.com/dramaapi/search?s={{key}}&page=1",
    "weight": 0
}
  • 正文下一页 URL 规则 (nextContentUrl)
    支持单个 url,支持 url 数组

  • WebViewJs (webJs)
    用于模拟鼠标点击等操作,必须有返回值(不为空,表示 webjs 执行成功,否则会无限循环),返回值会用于资源正则或正文中

    举个例子,在 webJs 中执行了 getDecode(),使正文部分解密:

json
"intro": "abstract",
"kind": "{{$.type_name}},{{$.catalog_name}}",
"lastChapter": "newest",
"name": "name",
"wordCount": "catalog_name"
json
{
    "bookSourceGroup": "阅读3.0书源合集",
    "bookSourceName": "斋书苑",
    "bookSourceType": 0,
    "bookSourceUrl": "https://www.zhaishuyuan.com",
    "bookUrlPattern": "",
    "customOrder": 11,
    "enabled": false,
    "enabledExplore": false,
    "exploreUrl": "男生书库::/shuku/0_1_0_0_0_{{page}}_0_0\n男频连载::/shuku/0_2_0_0_0_{{page}}_0_0",
    "lastUpdateTime": 0,
    "loginUrl": "",
    "ruleBookInfo": {
        "author": "@css:[property=og:novel:author]@content",
        "coverUrl": "@css:[property=og:image]@content",
        "intro": "@css:#bookintro@html",
        "kind": "@css:[property=og:novel:category]@content",
        "lastChapter": "@css:[property=og:novel:latest_chapter_name]@content",
        "name": "@css:[property=og:novel:book_name]@content",
        "tocUrl": "@css:[property=og:novel:read_url]@content",
        "wordCount": "@css:.count li:eq(3)>span@text"
    },
    "ruleContent": {
        "content": "all",
        "nextContentUrl": "",
        "webJs": "getDecode();${'#content').html();"
    },
    "ruleExplore": {
        "author": "/li[4]/a/text()",
        "bookList": "/u1[count(../u1)>10]",
        "bookUrl": "/li[3]/a/@href",
        "coverUrl": "##/book/(\d+)##https://img.zhaishuyuan.com/bookpic/s$1.jpg##",
        "intro": "/li[6]/text()",
        "kind": "/li[2]/text()##\\[|\\]",
        "lastChapter": "/span/a/text()",
        "name": "/li[3]/a/text()",
        "wordCount": "/li[5]/text()"
    },
    "ruleSearch": {
        "author": "/dd[2]/span[1]/text()",
        "bookList": "//*[@id=\"sitembox\"]/dl",
        "bookUrl": "/dt/a/@href",
        "coverUrl": "/img/@_src",
        "intro": "/dd[3]/html()",
        "kind": "/dd[2]/span[3]/text()",
        "lastChapter": "/dd[4]/a/text()",
        "name": "/h3/a//text()",
        "wordCount": "/dd[2]/span[4]/text()"
    },
    "ruleToC": {
        "chapterList": ":href=\"((/chapter/{^\"}*)\"{^>}*)<{^<}*)</a>{^<}*)\"",
        "chapterName": "$2",
        "chapterUrl": "$1,{\"webView\":true}",
        "nextToCUrl": "/strong/following-sibling::a/@href",
        "updateTime": "$3"
    },
    "searchUrl": "/search/,{ \n \"charset\": \"gbk\", \n \"method\": \"POST\", \n \"body\": \"page={{page}}&key={{key}}\"\n}",
    "weight": 0
}
  • 资源正则 (sourceRegex)
    用于嗅探
    一般情况下的无脑教程下:
    章节链接后面加 ,{"webView":true},不要洒散数的写成 tag.a@href,{"webView":true}$.link,{"webView":true},正确写法:tag.a@href##("#webView":true){{@@tag.a@href}, {"webView":true}tag.a@href@js:result+',{"webView":true}
    在有嗅探功能的浏览器(例如:via、x浏览器等)中,输入章节链接。注意千万别带 ,{"webView":true}
    媒体开始播放后使用浏览器的嗅探功能,查看资源的链接
    在资源正则里填写资源链接的正则,一般写 .*\.(mp3|mp4).* 这个就可以了
    最后在正文填写 <js>result</js>

    下示例,在正文嗅探 mp3 和 mp4 的资源:

json
{
    "bookSourceComment": "",
    "bookSourceGroup": "有声",
    "bookSourceName": "猫耳FM",
    "bookSourceType": 1,
    "bookSourceUrl": "https://www.missevan.com",
    "customOrder": 0,
    "enabled": false,
    "enabledExplore": true,
    "lastUpdateTime": 0,
    "ruleBookInfo": {},
    "ruleContent": {
        "content": "https://static.missevan.com/{f//*[contains(@class,\"pld-sound-active\")]}/@data-soundurl64}",
        "sourceRegex": "",
        "webJS": ""
    },
    "ruleExplore": {},
    "ruleSearch": {
        "author": "author",
        "bookList": "$.info.Datas",
        "bookUrl": "https://www.missevan.com/mdrama/drama/{{$.id}},{\"webView\":true}",
        "coverUrl": "cover",
        "intro": "abstract",
        "kind": "({{$.type_name}},{{$.catalog_name}}",
        "lastChapter": "newest",
        "name": "name",
        "wordCount": "catalog_name"
    },
    "ruleToc": {
        "chapterList": "@css:.scroll-list.btn-groups>a",
        "chapterName": "text",
        "chapterUrl": "hrer###,{\"webView\":true}"
    },
    "searchUrl": "https://www.missevan.com/dramaapi/search?s={{key}}&page=1",
    "weight": 0
}

9、补充说明

显示 js 的报错信息

javascript
(function (result) {
    try {
        // 处理 result
        // ...
        // 当返回结果为字符串时
        return result;
        // 当返回结果为列表时
        return list;
    } catch (e) {
        // 当返回结果为字符串时
        return "" + e;
        // 当返回结果为列表时
        return ["" + e]; // 列表对应名称处填 <js>""+result</js> 查看
    }
})(result);

请善用调试功能

  • 调试搜索
    输入关键字,例如:系统
  • 调试发现
    输入发现 URL,例如:月票榜:https://www.qidian.com/rank/yuepiao?page={(page)}
  • 调试详情页
    输入详情页 URL,例如:https://m.qidian.com/book/1015609210
  • 调试目录页
    输入目录页 URL,例如:++https://www.zhaishuyuan.com/read/30394
  • 调试正文页
    输入正文页 URL,例如:--https://www.zhaishuyuan.com/chapter/30394/20940996

无脑 {"webView":true} 很方便

特别注意

JSON.stringify() 方法时,需要保证 JSON 对象的 value 都是 JavaScript 的 String 类型(在阅读 3.0 中)


附:书源示例

书源一

json
{
    "bookSourceComment": "",
    "bookSourceGroup": "CSS; 正则",
    "bookSourceName": "/i说2016",
    "bookSourceType": 0,
    "bookSourceUrl": "https://www.xiaoshuo2016.com",
    "bookUrlPattern": "",
    "customOrder": 0,
    "enabled": true,
    "enabledExplore": false,
    "exploreUrl": "",
    "lastUpdateTime": 0,
    "loginUrl": "",
    "ruleBookInfo": {
        "author": "##:author\"[A\"]+\"([A\"]*)##$1##",
        "coverUrl": "##og:image\"[A\"]+\"([A\"]*)##$1##",
        "intro": "##:description\"[A\"]+\"([\wWW]*?)\"/##$1##",
        "kind": "##:category\"[A\"]+\"([A\"]*)##$1##",
        "lastChapter": "##_chapter_name\"[A\"]+\"([A\"]*)##$1##",
        "name": "##:book_name\"[A\"]+\"([A\"]*)##$1##",
        "toCUrl": ""
    },
    "ruleContent": {
        "content": "@css:.articleDiv p@textNodes##搜索.*手机访问|一秒记住.*].*阅读下载|<!\\[CDATA\\[|\\]\>",
        "nextContentUrl": ""
    },
    "ruleExplore": {},
    "ruleSearch": {
        "author": "@css:p:eq(2)>a@text",
        "bookList": "@css:li.clearfix",
        "bookUrl": "@css:.name>a@href",
        "coverUrl": "@css:img@src",
        "intro": "@css:.note.clearfix p@text",
        "kind": "@css:.note_text,p:eq(4)@text",
        "lastChapter": "@css:p:eq(3)@text",
        "name": "@css:.name@text"
    },
    "ruleToc": {
        "chapterList": "-:<li><a[A\"]+\"([A\"]*)\">([A<]*)",
        "chapterName": "$2",
        "chapterUrl": "$1",
        "nextToCUrl": ""
    },
    "searchUrl": "/modules/article/search.php?searchkey={{key}}&submit=&page={{page}},{ \n \"charset\": \"gbk\"\n}",
    "weight": 0
}

书源二

json
{
    "bookSourceComment": "",
    "bookSourceGroup": "XPath; 正则",
    "bookSourceName": "采集手册版",
    "bookSourceType": 0,
    "bookSourceUrl": "https://m.caimoge.com",
    "bookUrlPattern": "",
    "customOrder": 0,
    "enabled": true,
    "enabledExplore": false,
    "exploreUrl": "",
    "lastUpdateTime": 0,
    "loginUrl": "",
    "ruleBookInfo": {
        "author": "//*[@property=\"og:novel:author\"]/@content",
        "coverUrl": "//*[@property=\"og:image\"]/@content",
        "intro": "//*[@property=\"og:description\"]/@content",
        "kind": "//*[@property=\"og:novel:category\"]/@content",
        "lastChapter": "//*[@id=\"newList\"]//li[1]/a/text()",
        "name": "//*[@property=\"og:novel:book_name\"]/@content",
        "tocUrl": "//a[text()=\"阅读\"]/@href"
    },
    "ruleContent": {
        "content": "//*[@id=\"content\"]",
        "nextContentUrl": ""
    },
    "ruleExplore": {},
    "ruleSearch": {
        "author": "//dd[2]/text()",
        "bookList": "//*[@id=\"sitebox\"]/dl",
        "bookUrl": "//dt/a/@href",
        "coverUrl": "//img/@src",
        "kind": "//dd[2]/span/text()",
        "lastChapter": "",
        "name": "//h3/a/text()"
    },
    "ruleToC": {
        "chapterList": ":href=\"(/read[A\"]*html)\">([^<]*)",
        "chapterName": "$2",
        "chapterUrl": "$1",
        "nextTocUrl": "//*[@id=\"pageList\"]/*[position()>1]/@value"
    },
    "searchUrl": "/search.html,\n \"method\": \"POST\",\n \"body\": \"searchkey={(key)}\"\n}",
    "weight": 0
}

书源三

json
{
    "bookSourceComment": "",
    "bookSourceGroup": "JSon",
    "bookSourceName": "猎鹰小说网",
    "bookSourceType": 0,
    "bookSourceUrl": "http://api.book.lieying.cn",
    "customOrder": 0,
    "enabled": true,
    "enabledExplore": false,
    "header": "(\n \"User-Agent\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36\"\n)",
    "lastUpdateTime": 0,
    "ruleBookInfo": {},
    "ruleContent": {
        "content": "$.chapter.body"
    },
    "ruleExplore": {},
    "ruleSearch": {
        "author": "$.author",
        "bookList": "$..books[*]",
        "bookUrl": "/Book/getChapterListByBookId?bookId={$._id}",
        "coverUrl": "$.cover",
        "intro": "$.shortIntro",
        "kind": "$.minorCate",
        "lastChapter": "$.lastChapter",
        "name": "$.title"
    },
    "ruleToc": {
        "chapterList": "$.chapterInfo.chapters.[*]",
        "chapterName": "$.title",
        "chapterUrl": "$.link"
    },
    "searchUrl": "/Book/search?query={{key}}&start={{(page-1)*20}} &limit=40&device_type=android&app_version=165",
    "weight": 0
}

登录后发表评论

请先登录账号后再发表评论