跳转至

开源阅读网络请求说明

预计阅读时长 : 6 分钟

请求流程

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

请求方法

Legado 在进行网络请求时,涉及到的底层 JavaScript 方法分为四种,具体定义可查看源代码 ⧉

AnalyzeUrl 库

AnalyzeUrl ⧉ 是一个 Legado 中自定义的网络请求库,默认情况下网络请求使用的就是这个库及其封装的方法。

/* 使用 AnalyzeUrl 库进行网络请求
* @param urlStr/urlList 封装好的 url,可以带有请求头和请求体
* @return 返回请求结果
*/

// 直接返回 String
java.ajax(urlStr): String
java.ajaxAll(urlList: Array<String>): Array<StrResponse?>

// 返回 Response<String>,可调用 body() code() message() header() raw() toString() 方法
java.connect(urlStr): StrResponse

/* 
调用方法实例
*/

(()=>{
  if(page==1){
    let url='https://www.dmxsw.org/e/search/index.php,'+JSON.stringify({
    "method":"POST",
    "body":"show=title&classid=0&keyboard="+key
    });
    // 通过链式调用获取最终的重定向地址
    return java.put('surl',String(java.connect(url).raw().request().url()));
  } else {
    return java.get('surl')+'&page='+(page-1)
  }
})()

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 ⧉ 是一个用于处理验证码验证的库,主要用于唤起内置浏览器,处理需要手动进行验证的场景。

/* 使用内置浏览器打开链接,手动进行验证码验证操作,可用于网站防爬等场景
* @param url 要打开的链接
* @param title 浏览器的标题
* @param imageUrl 验证码图片链接
* @return 返回验证码验证结果
*/

// 使用内置浏览器打开链接
java.startBrowser(url: String, title: String)

// 使用内置浏览器打开链接,并异步等待网页结果,可通过 .body() 获取网页内容
java.startBrowserAwait(url: String, title: String): StrResponse

// 打开图片验证码对话框,等待返回验证结果
java.getVerificationCode(imageUrl: String): String

/* 
调用方法实例
*/

<js>
if(/验证码/.test(result)){
    var so=java.get("url"),
    su=source.key;
    c=java.head(baseUrl,{}).cookies();
    source.putLoginHeader(c);
    url=su+"/get_btwaf_captcha_base64?captcha="+Date.now();
    body=java.get(url,{}).body();
    img="data:image/png;base64,"+JSON.parse(body).msg;
    captcha=java.getVerificationCode(img);
    $=java.ajax(`${su}/Verification_auth_btwaf?captcha=${captcha}`);
    java.toast(JSON.parse($).msg);
    result=java.ajax(so);
    }
result;
</js>
class.line

请求类型

GET

一般情况下,网络请求使用普通的 GET 方法即可,只需要在对应的位置直接填入 URL。

如果部分页面对于使用的客户端有限制,例如只允许在手机上进行访问,那么可以尝试在 基础 - 请求头 中使用 JSON 格式增加客户端标识。

首先,我们来看一下请求头的最简内容,注意必须是 JSON 格式:

1
2
3
{
    "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",
}

对于复杂的情况,可使用 JavaScript 来拼接和生成请求头:

1
2
3
4
5
6
7
8
9
@js:
(()=>{
    let headers = {
        "referer": baseUrl,
        "x-requested-with": "mark.via",
        "accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
        "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"};
    return JSON.stringify(headers);
})()
IIFE 立即调用函数表达式
  • 定义箭头函数:() => {...}是一个箭头函数的简写形式。当箭头函数没有参数时,你需要在箭头=>之前提供一对空括号()来表示这是一个函数定义。这里的大括号{...}内是函数体,包含了函数的执行逻辑。
  • 立即调用函数表达式(IIFE):在箭头函数外面的一对括号(...)用于立即调用该箭头函数。这种模式称为立即调用函数表达式(IIFE)。通过在函数定义后紧跟一对括号(),JavaScript引擎会立即执行该函数。在这个例子中,外层的括号(() => {...})()不仅定义了一个函数,还立即执行了它。
  • 使用 JavaScript 方法时,请保证相关变量的类型都满足规范,是计算得到的尽量都用 String() 强转一下类型,以便 JSON.stringify() 转换字符串生成请求头成功。
  • 一般形式如下,charset 为 utf-8 时可省略,如果还是获取不成功可以到 中查看例如 <meta charset="gbk"> 的编码确定具体的编码

POST

对于需要填写表单后才能获取到目标页面,或者是直接调用 API 的网络请求,则一般会使用 POST 方法。

这个时候,就不能再使用 基础 - 请求头 字段,需要额外配置 methodbody 属性,并直接在 基础 - 源域名 中进行拼接。

1
2
3
4
https://www.baidu.com,{
     "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\"}",}

复杂情况同样可使用 JavaScript 先生成请求头,然后再拼接到 URL 之后:

1
2
3
4
5
6
7
8
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 = {
    "method": "POST",
    "headers": JSON.stringify(headers),
    "body": String(body),
    };
"https://www.baidu.com," + JSON.stringify(option)

除了 基础 - 源域名 之外,其他涉及到网络请求的位置,都需要通过 @js: 来调用 JavaScript 方法来拼接生成完整的 url。例如,搜索 - 搜索地址 中的 url 可以通过以下的代码进行拼接:

@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))

webView

如果增加了客户端标识还是无法获取内容,那就需要考虑使用 webView 方式模拟浏览器进行网络请求,以规避部分网站的反扒机制,以及抓取 Ajax 异步加载的内容。

对于基础 - 源域名,只需要在添加链接后添加额外 {"webView": true} 请求头信息就可以实现 webView 请求:

https://www.baidu.com,{"webView": true}

如果是其他的步骤中的网络请求,例如 目录-章节地址 中提取出来的 url,则需要将 {"webView": true} 加入到参数中,再通过 JS 拼接出带有参数的 url,来实现 webView 方式访问。

@css:a@href@js:result+','+JSON.stringify({"webView": true});