news 2026/6/9 8:36:51

novel-downloader规则扩展实战指南:从零构建自定义抓取规则

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
novel-downloader规则扩展实战指南:从零构建自定义抓取规则

novel-downloader规则扩展实战指南:从零构建自定义抓取规则

【免费下载链接】novel-downloader一个可扩展的通用型小说下载器。项目地址: https://gitcode.com/gh_mirrors/no/novel-downloader

novel-downloader是一个高度可扩展的通用型小说下载器,其核心扩展机制基于模块化的规则系统。本文将深入解析其规则扩展架构,指导开发者如何为新的小说网站构建自定义抓取规则,实现从概念理解到实践部署的完整技术流程。

一、规则系统架构深度解析

novel-downloader采用分层架构设计,将核心下载引擎与网站特定规则解耦。核心架构由BaseRuleClass基类定义统一接口,各网站规则通过继承或工厂函数实现具体逻辑。

1.1 核心基类设计

BaseRuleClass作为所有规则的抽象基类,定义了统一的生命周期方法:

export abstract class BaseRuleClass { public abstract bookParse(): Promise<Book>; public abstract chapterParse( chapterUrl: string, chapterName: string | null, isVIP: boolean, isPaid: boolean | null, charset: string, options: Record<string, any> ): Promise<ChapterParseObject>; public async run(): Promise<Book | undefined> { // 统一执行流程 } protected async preHook(): Promise<void> { /* 预处理钩子 */ } protected async initChapters(): Promise<Chapter[]> { /* 章节初始化 */ } protected postHook(): void { /* 后处理钩子 */ } }

1.2 规则目录结构

项目采用按网站类型分类的目录结构,便于维护和扩展:

src/rules/ ├── onePage/ # 单页式网站规则 │ ├── template.ts # 单页规则模板 │ ├── 69shuba.ts # 实际规则示例 │ └── ... ├── twoPage/ # 双页式网站规则 ├── special/ # 特殊网站规则 │ ├── original/ # 原创网站 │ └── reprint/ # 转载网站 └── lib/ # 通用工具库

二、规则开发实战:单页式网站模板解析

2.1 模板工厂函数模式

单页式规则采用工厂函数模式,通过mkRuleClass快速创建规则类:

// src/rules/onePage/template.ts export function mkRuleClass({ bookUrl, bookname, author, introDom, introDomPatch, coverUrl, aList, getContentFromUrl, contentPatch, // ... 其他配置参数 }: MkRuleClassOptions): PublicConstructor<BaseRuleClass> { return class extends BaseRuleClass { // 具体实现 }; }

2.2 实际规则实现示例

以69书吧为例,展示完整规则实现:

// src/rules/onePage/69shuba.ts import { htmlTrim } from "../../lib/cleanDOM"; import { rm2 } from "../../lib/dom"; import { nextPageParse } from "../../lib/rule"; import { mkRuleClass } from "./template"; export const c69shuba = () => mkRuleClass({ bookUrl: location.href, bookname: document.querySelector("#info h1")!.innerText.trim(), author: document.querySelector("#info > p")!.innerText .trim() .replace(/^作\s*者\s*[::]\s*/u, ""), introDom: document.querySelector("#intro")!, introDomPatch: (_) => _, coverUrl: document.querySelector("#fmimg img")!.src, aList: document.querySelectorAll("#list > dl > dt:last-of-type ~ a"), async getContentFromUrl(chapterUrl, chapterName, charset) { const { contentRaw } = await nextPageParse({ chapterName, chapterUrl, charset, selector: "#booktxt", contentPatch(content, doc) { rm2([/本章阅读完毕/], content); htmlTrim(content); return content; }, getNextPage(doc) { const nextPageLink = doc.querySelector( 'div.bottem1 > a[rel=next]' ) as HTMLAnchorElement; if (nextPageLink && nextPageLink.innerText.includes("下一页")) { return nextPageLink.href; } return ""; }, continueCondition(content, nextLink) { return nextLink !== "" && /_\d.html/.test(nextLink); }, }); return contentRaw; }, contentPatch: (content) => content, });

三、规则实现关键技术点

3.1 章节列表提取策略

章节列表提取需要精确分析网站DOM结构,使用CSS选择器定位章节链接:

// 章节选择器示例 aList: document.querySelectorAll("#list > dl > dt:last-of-type ~ a"), // 自定义章节名称提取 getAName: (aElem) => { // 处理特殊格式的章节标题 return aElem.innerText.replace(/第\d+章\s*/, "").trim(); },

3.2 内容提取与清理

内容提取需要处理多种复杂情况,包括分页加载、广告过滤等:

// 分页内容提取 async getContentFromUrl(chapterUrl, chapterName, charset) { const { contentRaw } = await nextPageParse({ chapterName, chapterUrl, charset, selector: "#booktxt", contentPatch(content, doc) { // 移除广告和无关元素 rm2([/本章阅读完毕/, /请收藏本站/, /广告/], content); htmlTrim(content); return content; }, getNextPage(doc) { // 下一页链接提取逻辑 const nextLink = doc.querySelector('a.next'); return nextLink?.href || ""; }, continueCondition(content, nextLink) { // 继续提取下一页的条件 return nextLink !== "" && !nextLink.includes("javascript:"); }, }); return contentRaw; }

3.3 反爬机制处理

对于采用复杂反爬措施的网站,需要特殊处理:

// 字体加密处理示例(参考jjwxcFontDecode.ts) import { decodeJJWXCFont } from "../../lib/jjwxcFontDecode"; const contentPatch = (content) => { // 检测并解码字体加密内容 const decodedContent = decodeJJWXCFont(content); return decodedContent; }; // 图片验证码处理 import { OCRDecoder } from "../../lib/decoders/OCRDecoder"; async function handleCaptcha(imageUrl) { const ocr = new OCRDecoder(); const captchaText = await ocr.decodeImage(imageUrl); return captchaText; }

四、特殊网站规则实现

4.1 Cloudflare防护网站处理

对于采用Cloudflare防护的网站,需要特殊的技术方案:

// src/rules/special/reprint/sbxh.ts // 处理Cloudflare + Shadow DOM的复杂场景 export const sbxh = () => mkRuleClass({ // ... 省略其他配置 getContentFromUrl: async (chapterUrl, _name, _charset) => { const result = await fetchChapterViaIframe(chapterUrl); if (!result) return null; if (result.captcha) return createCaptchaMessage(); return parseViewerHtml(result.html); }, concurrencyLimit: 1, sleepTime: 1000, });

4.2 多页索引网站处理

对于章节分布在多个索引页的网站,需要实现多页遍历:

// src/rules/onePageWithMultiIndexPage/template.ts export function mkMultiIndexRuleClass({ // ... 基础配置 getNextIndexPage, // 获取下一页索引页 continueIndexCondition, // 继续遍历索引页的条件 }) { return mkRuleClass({ // ... 基础配置 async bookParse() { const allChapters = []; let currentIndexUrl = bookUrl; do { const doc = await getHtmlDOM(currentIndexUrl, charset); const chapters = extractChaptersFromPage(doc); allChapters.push(...chapters); currentIndexUrl = getNextIndexPage(doc); } while (continueIndexCondition(currentIndexUrl)); // ... 构建Book对象 }, }); }

五、规则测试与验证

5.1 本地开发测试

开发新规则时,建议使用本地开发环境进行测试:

// 测试脚本示例 import { c69shuba } from "./src/rules/onePage/69shuba"; // 模拟环境测试 const rule = c69shuba(); const book = await rule.bookParse(); console.log(`书籍名称: ${book.bookname}`); console.log(`章节数量: ${book.chapters.length}`);

5.2 集成测试框架

项目提供E2E测试框架,确保规则质量:

// test/e2e-validate.ts describe("69shuba规则测试", () => { test("应正确提取书籍信息", async () => { const rule = c69shuba(); const book = await rule.bookParse(); expect(book.bookname).toBeTruthy(); expect(book.author).toBeTruthy(); expect(book.chapters.length).toBeGreaterThan(0); }); test("应正确提取章节内容", async () => { const rule = c69shuba(); const chapter = await rule.chapterParse( "https://www.69shuba.com/book/123/1.html", "第一章", false, false, "utf-8", {} ); expect(chapter.contentText).toBeTruthy(); expect(chapter.contentText.length).toBeGreaterThan(100); }); });

六、规则部署与贡献流程

6.1 规则注册机制

新规则需要在src/rules.ts中注册:

// src/rules.ts import { c69shuba } from "./rules/onePage/69shuba"; import { exampleRule } from "./rules/onePage/example"; export const rules = [ // ... 现有规则 c69shuba(), exampleRule(), ];

6.2 规则匹配逻辑

系统通过URL正则匹配自动选择合适的规则:

// 规则匹配示例 const matchedRule = rules.find(rule => rule.url.test(currentUrl) );

6.3 贡献指南

提交新规则时需遵循以下规范:

  1. 代码质量:遵循项目编码规范,使用TypeScript严格类型
  2. 错误处理:完善的异常处理和日志记录
  3. 性能优化:合理设置并发限制和请求间隔
  4. 文档完善:提供规则说明和使用示例
  5. 测试覆盖:包含完整的测试用例

七、高级扩展技巧

7.1 自定义内容处理管道

通过contentPatch函数实现自定义内容处理:

contentPatch: (content) => { // 移除特定广告元素 const adSelectors = [ '.ad-container', '[class*="advert"]', '[id*="ad"]' ]; adSelectors.forEach(selector => { content.querySelectorAll(selector).forEach(el => el.remove()); }); // 标准化段落格式 content.querySelectorAll('p').forEach(p => { p.style.margin = '1em 0'; }); return content; },

7.2 动态配置支持

支持运行时配置,增强规则灵活性:

export const createConfigurableRule = (config: RuleConfig) => mkRuleClass({ bookUrl: config.bookUrl, bookname: config.booknameSelector, author: config.authorSelector, aList: config.chapterListSelector, getContentFromUrl: async (url, name, charset) => { // 使用配置的选择器 const selector = config.contentSelector; const doc = await getHtmlDOM(url, charset); return doc.querySelector(selector); }, concurrencyLimit: config.concurrency || 10, sleepTime: config.sleepTime || 50, });

八、最佳实践总结

  1. 选择正确的规则模板:根据网站结构选择onePagetwoPagespecial模板
  2. 精确的DOM选择器:使用稳定的CSS选择器,避免依赖易变的class名称
  3. 完善的错误处理:处理网络异常、DOM变化等边界情况
  4. 性能优化:合理设置并发限制,避免对目标网站造成过大压力
  5. 代码复用:充分利用lib目录下的通用工具函数
  6. 持续维护:定期检查规则有效性,及时适配网站改版

通过本文的深度解析,开发者可以全面掌握novel-downloader的规则扩展机制。该系统的模块化设计和丰富的工具库使得为新的小说网站添加支持变得高效而规范。无论是简单的单页网站还是复杂的Cloudflare防护站点,都可以通过适当的规则实现稳定可靠的内容抓取。

图:novel-downloader规则扩展架构图,展示核心基类、规则工厂和具体实现的关系

图:规则处理流程图,展示从URL匹配到内容提取的完整处理流程

【免费下载链接】novel-downloader一个可扩展的通用型小说下载器。项目地址: https://gitcode.com/gh_mirrors/no/novel-downloader

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 8:31:06

3步深度优化Windows系统:开源工具Win11Debloat实战指南

3步深度优化Windows系统&#xff1a;开源工具Win11Debloat实战指南 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declutter and c…

作者头像 李华
网站建设 2026/6/9 8:25:50

DS18B20单总线通信避坑指南:从Proteus仿真到实物焊接的5个常见问题

DS18B20单总线通信避坑指南&#xff1a;从Proteus仿真到实物焊接的5个常见问题第一次在Proteus里看到DS18B20温度传感器显示85℃时&#xff0c;我花了整整两天时间排查代码问题。直到翻遍数据手册才发现&#xff0c;这竟然是芯片上电的默认状态。这种"坑"在单总线通信…

作者头像 李华
网站建设 2026/6/9 8:23:06

FPGA开发用SPI模式0主从通信Verilog工程,含ModelSim可运行仿真环境

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;这套Verilog工程专为FPGA初学者和嵌入式硬件开发者设计&#xff0c;实现标准SPI模式0&#xff08;CPOL0&#xff0c;CPHA0&#xff09;下的主从通信功能。主机模块支持32位十六进制数据逐位发送&#xff0c;采用…

作者头像 李华