开源阅读网络请求说明
预计阅读时长 : 14 分钟
请求流程
Legado 通过书源配置,构建了一个连贯的网络请求链条,链条中的每一步都会将提取的 url 以 baseUrl 字符串传递给下一步,同时通过 result 给下一步传递 url 返回的响应内容以供内容提取。
graph LR
A[基本 - 源域名+请求头] --> B(搜索地址) --> C(搜索 - 详情地址)
A --> D(发现地址) --> E(发现 - 详情地址)
C --> F[详情 - 目录地址]
E --> F
F --> G(目录 - 章节地址 + 翻页规则)
G --> H(章节 - 章节规则 + 翻页规则)
流程图说明
- 为了更加清晰的展示网络请求的流程,这里将描述的文字进行了部分调整,未与配置界面一一对应。
- 实际上,
搜索地址
在 搜索
Tab 中,发现地址
在 发现
Tab 中,从理解的角度上其实放入 基本
Tab 中更为合适。
章节
实际对应的是 正文
Tab,章节规则
对应的是 正文规则
。
针对不同类型的网络请求,也可以通过给 url 配置对应的变量以及 JSON 化的请求参数,精细化的调整请求类型和请求方法。
常用变量
网络请求中的最常用的两大变量分别是 baseUrl
和 result
,它们分别代表了请求的链接地址和请求的响应结果。
baseUrl
: 上一步生成的当前步骤的原始 url
详情
Tab 的 baseUrl 就是由 搜索 - 详情地址
或者 发现 - 详情地址
中的规则提取的。
result
: 上一步网络请求的原始响应结果
- 独立使用代表当前步骤 baseUrl 的响应内容。
- 在组合规则中使用代表上一步规则的结果,例如
@css:a@href@js:result+','+JSON.stringify({"webView": true});
中的 result 就是上一步 @css:a@href
抓取到的 href 链接的值。
- 底层逻辑是通过 Promise 异步请求的
.then
处理程序(handler)链进行传递 result
其他的常用变量值还包括:
java
: 当前类
cookie
: cookie 操作类
cache
: 缓存操作类
source
: 书源类
book
: 书籍类
src
: 内容源码
title
: 当前标题
chapter
: 当前目录类
nextChapterUrl
: 下一章节 url
请求方式
普通请求
一般情况下,网络请求使用最普通的 GET 方法即可,只需要在对应的属性值中直接填入 URL 即可。
如果部分页面对于使用的客户端有限制,例如只允许在手机上进行访问,那么可以尝试在 基础 - 请求头
中使用 JSON 格式增加客户端标识。
| {
"User-Agent": "Mozilla/5.0 (Linux; Android 10; PACM00 Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.79 Mobile Safari/537.36"
}
|
模拟浏览器请求
上述格式的 URL 使用的是后面提到的 AnalyzeUrl
库。在某些网站的反扒机制中,此类网络需求无法获取到期望的内容。
这种情况下,我们可以使用 BackstageWebView
库,即通过在链接后的 ,{}
中添加参数 "webView": true
来调用 webView
模拟浏览器行为,以规避反扒机制。
| https://www.baidu.com,{"webView": true}
|
如果是其他的步骤中的网络请求,例如 目录-章节地址
中提取出来的 url,则可以通过 JS 拼接出带有参数的 url,来实现 webView 方式访问。
添加 webView 参数示例 |
---|
| @css:a@href@js:result+','+JSON.stringify({"webView": true});
|
请求参数
这里的 ,{}
中填写的就是 URL 参数,完整的 URL 参数格式如下:
| data class UrlOption(
val webView: Any?,
val charset: String?,
val headers: Any?,
val method: String?,
val body: Any?,
val type: String?,
val js: String?,
val retry: Int = 0 //重试次数
)
|
刚刚我们看了 webView
参数的作用,下面我们来看看如何通过其他参数配置,来实现更为复杂的网络调用。
配置 charset
charset 为 utf-8
时可省略,一般获取的内容为乱码时,可以考虑配置 charset 为 gbk
。
| https://www.baidu.com,{"charset": "GBK"}
|
简单 header 配置可以直接通过字符串在参数中进行赋值,如果内容比较多或者比较复杂,也可以使用 JSON.stringify
来进行拼接。
| @js:
let ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36";
let headers = {
"headers": {"User-Agent": ua}
};
result = "https://www.baidu.com," + JSON.stringify(headers)
|
通过 POST 方法请求
对于需要填写表单后才能获取到目标页面,或者是直接调用 API 的网络请求,则一般会使用 POST 方法。
使用 POST 方法时,需要额外配置 method
和 body
参数,type
参数默认设置为 "application/json"
,可以省略。
POST 完整请求示例 |
---|
| https://www.baidu.com,{
"method": "POST",
"body": "bid=10086",
}
|
复杂情况同样可使用 JavaScript 先生成请求头,然后再拼接到 URL 之后:
POST 复杂请求示例 |
---|
| let headers = {
"User-Agent":"Mozilla/5.0 (Linux; Android 10; PACM00 Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.79 Mobile Safari/537.36",
};
let body = "bid=" + "10086";
let option = {
"charset": "gbk",
"method": "POST",
"body": String(body),
"headers": headers,
"webView": true,
};
result = "https://www.baidu.com," + JSON.stringify(option);
|
通过 JS 处理请求
通过 js 参数,可以在解析 url 时进行更复杂的处理。
网络请求对象定义 |
---|
| java.post(url: String, body: String, headerMap: Map<String, String>): Connection.Response
java.get(url: String, headerMap: Map<String, String>): Connection.Response
java.head(url: String, headerMap: Map<String, String>): Connection.Response
|
通过 JS 处理请求示例 |
---|
| https://www.baidu.com,{"js":"java.headerMap.put('xxx', 'yyy')"}
https://www.baidu.com,{"js":"java.url=java.url+'yyyy'"}
|
除了 基础 - 源域名
之外涉及到网络请求的位置,也需要通过 @js:
来调用 JavaScript 方法来拼接生成完整的 url。例如,搜索 - 搜索地址
中的 url 可以通过以下的代码进行拼接:
POST 通过 JS 处理请求示例 |
---|
| @js:
let url=source.key+"/aoyuges.php,";
let body=`type=articlename&s=${key}&submit=`;
let option=JSON.stringify({
"method": "POST"
"body": String(body),
"charset": "GBK",
});
// 设置变量键值对,并返回变量值
java.put("url",String(url+option))
|
底层库资料
Legado 在进行网络请求时,涉及到的底层 JavaScript 方法分为四种,具体定义可查看源代码 ⧉。
AnalyzeUrl 库
AnalyzeUrl ⧉ 是一个 Legado 中自定义的网络请求库,默认情况下网络请求使用的就是这个库及其封装的方法。
BackstageWebView 库
BackstageWebView ⧉ 是一个基于 Android WebView 的封装库,使用无头浏览器进行网络请求,可以通过在 url 中拼接 {"webView": true}
请求头参数进行调用。
| /* 使用 webView 访问网络,模拟浏览器行为获取内容
@param html 直接用 webView 载入的 html, 如果 html 为空直接访问url
@param url html 内如果有相对路径的资源不传入 url 访问不了
@param js 用来取返回值的 js 语句, 没有就返回整个源代码
@return 返回 js 获取的内容
*/
// 使用 webView 获取内容
java.webView(html: String?, url: String?, js: String?): String?
// 使用 webView 获取跳转 url
java.webViewGetOverrideUrl(html: String?, url: String?, js: String?, overrideUrlRegex: String): String?
// 使用 webView 获取资源 url
java.webViewGetSource(html: String?, url: String?, js: String?, sourceRegex: String): String?
|
| @css:a@href
@js:
let wv={"webView": true};
result+','+JSON.stringify(mv);
|
JSoup 库
JSoup ⧉ 是一个用于解析 HTML 和 XML 文档的 Java 库。Legado 中通过 JSoup 库进行网络请求时,主要用于实现搜索后返回链接的重定向拦截。
| /* 使用 JSoup 库进行网络请求,主要用于实现重定向拦截
* @param url 请求地址
* @param headerMap 请求头
* @param body 请求体
* @return 返回请求结果
*/
java.head(url: String, headerMap: Map<String, String>): Connection.Response
java.get(url: String, headerMap: Map<String, String>): Connection.Response
java.post(url: String, body: String, headerMap: Map<String, String>): Connection.Response
|
| (()=>{
let base='https://www.dmxsw.org/e/search/';
if(page==1){
let url=base+'index.php';
let body='show=title&classid=0&keyboard='+key;
return base+java.put('surl',java.post(url,body,{}).header("Location").replace('?','<?,index.php?page={{page-1}}&>');
} else {
return base+java.get('surl')+'&page='+(page-1);
}
})()
|
SourceVerificationHelp 库
SourceVerificationHelp ⧉ 是一个用于处理验证码验证的库,主要用于唤起内置浏览器,处理需要手动进行验证的场景。