刚在B站看完《约会大作战》,觉得不错,听说还有轻小说,但是苦于找不到资源,后来发现一个网站有,但是提供下载的是一个TXT文件,就是说它把二十多册的内容合在一个TXT中了,看它网站上的目录还是比较清晰的,那我何不自己写一个脚本来按书名下载呢
网页截图: 脚本下载效果截图:cheerio 用于分析下载下来的HTML页面源代码
request 用于下载相应的页面
iconv-lite 用于对文本进行转码操作(这里用来将gbk转为utf8)
getContent.js
用于获取小说每一章节的内容getPage.js
用于获取(下载)所需求的HTML页面getUrlList.js
用于获取所有章节对应的url,以便于后面分类有序下载,获取相应内容writeFile.js (主入口)
用于将获取的内容写入文件中(保存在src文件夹中)那个网站使用的是gbk编码,但是我需要的是utf8编码,这个问题可以使用第三方模块 iconv-lite 来进行处理
估计那个网站访问的人不算多,所以服务器的性能可能不太高,我计算过,由于nodejs为异步的,所以会同时一次性发送192个请求,如果不进行处理,那么那个服务器最多只能响应170多个,这个可以通过setTimeout来进行分批请求解决
ps:如果直接从网站下载合在一个txt文件的小说的话,也是比较慢的
返回的json数据如下
// writeFile.js const fs = require('fs'); const getUrlList = require('./getUrlList'); const path = require('path'); const getContent = require('./getContent'); getUrlList.getUrlList((data) => { // 解析json格式的数据 const bookList = JSON.parse(data); // 获取所有的书名,用于后面的遍历与创建文件夹 let bookListKeys = Object.keys(bookList); // 由于前面提到的第二个请求数问题,这里分为三波进行下载 let s1 = bookListKeys.slice(0, bookListKeys.length / 3); let s2 = bookListKeys.slice(bookListKeys.length / 3, (bookListKeys.length/3)*2); let s3 = bookListKeys.slice((bookListKeys.length/3)*2, bookListKeys.length); s1.forEach((bookName, key) => { // 根据书名创建相应的文件夹 fs.mkdir('./src/' + bookName, (err) => { // 根据章节名获取内容写入文件 bookList[bookName].forEach((chapter, key) => { getContent.getContent(chapter.charUrl, (data) => { // 这里写的原因是:书名中会包含'/'对路径的生成有影响,所以对其进行转义 fs.writeFile(path.join(__dirname, 'src', bookName, (chapter.charName).replace(/\//g, ':') + '.txt'), data, (err) => { if (err) throw err; // 输出完成的章节名 console.log("finish: " + chapter.charName ); }); }); }); }); }); // 后面分别在30s后与60s后再次发起请求,不过好像可以再缩减短一点 s2.forEach((bookName, key) => { fs.mkdir('./src/' + bookName, (err) => { if (err) throw err; setTimeout(() => { bookList[bookName].forEach((chapter, key) => { getContent.getContent(chapter.charUrl, (data) => { fs.writeFile(path.join(__dirname, 'src', bookName, (chapter.charName).replace(/\//g, ':') + '.txt'), data, (err) => { if (err) throw err; console.log("finish: " + chapter.charName ); }); }); }); }, 30000); }); }); s3.forEach((bookName, key) => { fs.mkdir('./src/' + bookName, (err) => { if (err) throw err; setTimeout(() => { bookList[bookName].forEach((chapter, key) => { getContent.getContent(chapter.charUrl, (data) => { fs.writeFile(path.join(__dirname, 'src', bookName, (chapter.charName).replace(/\//g, ':') + '.txt'), data, (err) => { if (err) throw err; console.log("finish: " + chapter.charName ); }); }); }); }, 60000); }); }) });正好清明节没事干就折腾了一下,碰到很多坑,不过也是蛮有意思的,代码美中不足的就是出现了恐怖的 ‘回调金字塔’ ,后面会用Promise进行修改