[问题反馈] SearchParser.java referer 末尾携带 / 导致 HTTP 400
提交前自查
- 我已下载最新版本测试,确认问题依然存在。
- 我已认真阅读 使用说明。
- 我已查阅 常见问题,确认没有重复的讨论。
- 我已查阅 书源一览。
- 我已确保 已有问题 中无类似 issue(否则请在已有的 issue 内进行讨论)
- 我已为当前 Issue 拟定了一个简洁明了的标题。
操作系统名称及版本号 (在终端执行命令以查看,Windows:winver,Linux:lsb_release -a,macOS:sw_vers)
Windows 10 22H2 (19045.7548)
so-novel 发行版
sonovel-windows.tar.gz
so-novel 安装/运行方式
手动下载压缩包
配置信息
{
"version": "1.11.0",
"autoUpdate": 0,
"ghProxy": "",
"cfBypass": "",
"downloadPath": "downloads",
"extName": "txt",
"txtEncoding": "UTF-8",
"preserveChapterCache": 0,
"enableProgressbar": 1,
"language": "zh-CN",
"activeRules": "custom.json",
"sourceId": -1,
"searchLimit": 30,
"searchFilter": 0,
"concurrency": -1,
"minInterval": 200,
"maxInterval": 400,
"enableRetry": 1,
"maxRetries": 3,
"retryMinInterval": 2000,
"retryMaxInterval": 4000,
"webEnabled": 0,
"webPort": 7765,
"qidianCookie": "",
"proxyEnabled": 0,
"proxyHost": "127.0.0.1",
"proxyPort": 7890
}问题描述
⚠️ 以下内容由 AI(Reasonix)生成,经人工审核后发布。
环境
- SoNovel v1.11.0
- Windows 10
- 自定义
custom.json,包含两个书源
现象
搜索关键词「凡人」时,爱丽丝书屋(alicesw.com)正常返回 10 条结果,但第一版主(m.diyibanzhu.website)完全静默——无结果、无错误信息,该书源像被跳过一样。
但是:CLI 下载模式(-u https://m.diyibanzhu.website/list/5431.html)完全正常,章节下载、正文提取均无误。
已排除的因素
经过 30+ 次配置变体测试和源码阅读(见下方),以下因素已排除:
| 排除项 | 验证方式 |
|---|---|
search.url 中 %s 替换失败 |
逗号格式测试中 URL 正确显示 wd=凡人&objectType=2;源码 processUrl 仅做 url.formatted(keyword) |
POST data 格式问题 |
JSON、严格 JSON、原始 URL-encoded 三种格式全部测试;buildData 源码已确认 |
| CSS 选择器不匹配 | li.column-2 → ul.txt-list li → a.name → a → li 全试过 |
search-filter 过滤 |
已设为 0 |
| 书源被跳过 | source-id = 1 强制定向测试证明源 1 确实被搜索 |
@js: URL 构造 |
JsCaller 模板 function func(r) { %s; return r; } 要求赋值 r = ...,但即使正确赋值书源也消失 |
| Content-Type 过滤 | 源码中无任何 Content-Type 检查 |
| Request Body 类型 | buildData 使用 FormBody → application/x-www-form-urlencoded |
| 服务器可达性 | curl 同 URL/POST body → HTTP 200 + 10 条(8814 bytes),含 gzip 测试亦正常 |
| 超时 | 显式设置 timeout: 10000 无效 |
| 书源顺序 | 交换两个书源顺序后现象相同 |
关键规律
所有语法有错误的配置(JSON 不合法、URL 含非法字符如逗号)→ 书源出现 + 明确错误信息
所有语法正确的配置 → 书源完全消失,无任何输出
这说明书源确实被搜索了,HTTP 请求成功发出、服务器响应了,但 getSearchResults() 返回空列表。
相关源码分析
已阅读以下源文件(可提供完整代码):
SearchParser.java—parse()和getSearchResults()方法HtmlExtractor.java—extract()和select()方法CrawlUtils.java—buildData()和request()方法Rule.java— 数据模型
SearchParser.parse() 流程:
// 1. 构造请求
String searchUrl = processUrl(r.getUrl(), keyword); // url.formatted(keyword)
Request.Builder builder = new Request.Builder().url(searchUrl);
if ("post".equalsIgnoreCase(r.getMethod())) {
builder.post(CrawlUtils.buildData(r.getData(), keyword)); // FormBody
}
// 2. 发送请求
resp = CrawlUtils.request(httpClient, builder, r.getTimeout());
// 3. 解析响应
String body = processResultWithJs(resp.peekBody(Long.MAX_VALUE).string(), r.getResult());
document = Jsoup.parse(body, r.getBaseUri());
// 4. 提取结果
List<SearchResult> firstPageResults = getSearchResults(null, resp);getSearchResults() 中关键过滤逻辑:
Elements resultEls = document.select(resultSelector);
List<Element> limitResultEls = resultEls.stream()
.filter(e -> StrUtil.isNotEmpty(HtmlExtractor.extract(e, r.getBookName())))
.limit(...)
.toList();怀疑方向
源码层面未发现明显 bug。但以下线索值得关注:
第一版主返回
Content-Type: application/xhtml+xml(含<?xml version="1.0"?>声明和 XHTML Mobile DOCTYPE),虽然源码无 Content-Type 检查,但 Jsoup 的parse(String)在遇到 XML 声明 + XHTML DOCTYPE 时内部行为可能有差异同一站点的书详情页(CLI 下载)能正常解析——下载用
BookParser也是Jsoup.parse(String),但书详情页和搜索页的结构/DOCTYPE 完全相同。两者的区别仅在于:BookParser 直接用selectFirst("h1")而 SearchParser 多了filter(e -> ...)一步result选择器设为"li"仍无结果——即使是页面上必定存在的通用元素也匹配不到,说明书页的 Jsoup DOM 本身可能为空或异常
复现步骤
- 创建
custom.json,内容如下:
[
{
"url": "https://m.diyibanzhu.website",
"name": "第一版主",
"search": {
"url": "https://m.diyibanzhu.website/wap.php?action=search&wd=%s&objectType=2",
"method": "get",
"result": "li.column-2",
"bookName": ".right a.name",
"author": "p.info"
},
"book": { "url": "/list/(.*?).html", "bookName": "h1", "author": "p.info" },
"toc": { "baseUri": "https://m.diyibanzhu.website", "item": "/html//ul[@class='list']//a[contains(@href,'/view/')]" },
"chapter": { "title": ".bookname > h1", "content": "#nr1", "paragraphTagClosed": false, "paragraphTag": "<br>+", "filterTxt": "本章未完.*?>>|【\\d+】", "filterTag": "script, style" }
}
]config.ini设为active-rules = custom.json,search-filter = 0启动 SoNovel,搜索「凡人」
预期:应显示 10 条搜索结果;实际:书源完全无输出
复现步骤
Source: freeok/so-novel