小说下载器
一个可扩展的通用型小说下载器。
使用方法
特别提醒:本脚本与Greasemonkey脚本管理器不兼容。本脚本执行下载任务时将播放无声音频,以保证脚本后台运行时不被休眠。
如果本脚本支持该小说网站,当打开小说目录页时,网页右上角会出现下载图标,点击该图标即可开始下载。
如果你要下载的小说章节较多,等待时间可能较长,此时请耐心等待。
你通过右下角进度条了解当前下载进度,或者按下 F12,打开网页控制台查看当前下载状态。
下载完成后,本脚本将会自动下载一个TXT文档及由HTML文件及图片组成的ZIP压缩包。
TXT文档请使用记事本或其它阅读软件进行阅读。
ZIP压缩包,请在解压后,直接双击打开HTML文件(ToC.html
为目录文件)进行阅读。
目前支持小说网站
特别提醒:如欲下载支持列表中网站的付费章节,请登录相应网站帐户,并确定已购买相应付费章节。未登录网站帐户,或未购买的付费章节,下载时将直接忽略,无法进行下载。
高阶使用技巧
自定义筛选函数
如欲只下载部分章节,请在点击运行按钮前,按下 F12 打开开发者工具,在 Window
下创建自定义筛选函数 chapterFilter
。
declare enum Status {
pending = 0,
downloading = 1,
failed = 2,
finished = 3,
aborted = 4
}
class Chapter {
bookUrl: string;
bookname: string;
chapterUrl: string;
chapterNumber: number;
chapterName: string | null;
isVIP: boolean;
isPaid: boolean | null;
sectionName: string | null;
sectionNumber: number | null;
sectionChapterNumber: number | null;
chapterParse: ruleClassNamespace.chapterParse;
charset: string;
status: Status;
retryTime: number;
contentRaw: HTMLElement | null;
contentText: string | null;
contentHTML: HTMLElement | null;
contentImages: attachmentClass[] | null;
constructor(bookUrl: string, bookname: string, chapterUrl: string, chapterNumber: number, chapterName: string | null, isVIP: boolean, isPaid: boolean | null, sectionName: string | null, sectionNumber: number | null, sectionChapterNumber: number | null, chapterParse: ruleClassNamespace.chapterParse, charset: string);
init(): Promise<chapterParseObject>;
private parse;
}
interface chapterFilter {
(chapter: Chapter): boolean;
}
自定义筛选函数示例:
只下载该本小说前100章内容:
function chapterFilter(chapter) {
return chapter.chapterNumber <= 100
}
只下载第一卷内容:
function chapterFilter(chapter) {
return chapter.sectionNumber === 1
}
只下载章节名称中含有“武器”的章节:
function chapterFilter(chapter) {
return chapter.chapterName.includes("武器")
}
开发
根据 ruleClass
接口实现相应网站解析规则 Class,并在 rules.ts
中添加相应选择规则。
interface BookAdditionalMetadate {
cover?: attachmentClass;
attachments?: attachmentClass[];
tags?: string[];
lastModified?: number;
serires?: string;
seriresNumber?: number;
ids?: string[] | string;
publisher?: string;
languages?: string;
}
class attachmentClass {
url: string;
name: string;
mode: "naive" | "TM";
headers?: {
[index: string]: string;
};
private defaultHeader;
status: Status;
retryTime: number;
imageBlob: Blob | null;
constructor(imageUrl: string, name: string, mode: "naive" | "TM");
init(): Promise<Blob | null>;
private downloadImage;
private tmDownloadImage;
}
interface bookParseObject {
bookUrl: string;
bookname: string;
author: string;
introduction: string | null;
introductionHTML: HTMLElement | null;
additionalMetadate: BookAdditionalMetadate;
chapters: Chapter[];
}
interface chapterParseObject {
chapterName: string | null;
contentRaw: HTMLElement | null;
contentText: string | null;
contentHTML: HTMLElement | null;
contentImages: attachmentClass[] | null;
}
declare namespace ruleClassNamespace {
interface bookParse {
(): Promise<bookParseObject>;
}
interface chapterParse {
(chapterUrl: string, chapterName: string | null, isVIP: boolean, isPaid: boolean | null, charset: string): Promise<chapterParseObject>;
}
}
interface ruleClass {
imageMode: "naive" | "TM";
charset?: string;
concurrencyLimit?: number;
maxRunLimit?: number;
bookParse(chapterParse: ruleClassNamespace.chapterParse): Promise<bookParseObject>;
chapterParse(chapterUrl: string, chapterName: string | null, isVIP: boolean, isPaid: boolean | null, charset: string): Promise<chapterParseObject>;
}
License
AGPL-3.0