作为一个可以让JavaScript运行在服务端的平台,近年来NodeJS不可谓不火,于是乎前端崽们多少都会一些。今儿,我就在学习之中分享一个NodeJS写的抓取笔趣阁小说的爬虫。
前言
因为个人比较喜欢看小说,所以笔趣阁就成了我看小说的一个重要途径。虽然笔趣阁是免费的一个平台,但是并没有提供下载小说的方式。
所以我就在想是否可以通过我所学的NodeJS
知识来爬取上面的小说,所以就有下面的内容了。
相关技术
除了需要掌握NodeJS
以外,还需要借助第三方库来帮助编码,毕竟站在巨人上才能看的更远。这里,我选择的是 axios 和 cheerio 两个库。
aixos
: 这个库我就不必介绍了,写前端的都懂;cheerio
: 这个库简单一句话介绍就是服务端的jQuery
。
为什么没有用比较新的 puppeteer 库?
这个库网上都比较推荐,能比较方便的爬取异步渲染的数据。但我使用puppeteer
开发爬虫发现一个问题:当请求的页面过多时,占用的内存会变很多。即使一个简单的小说网站也有上千个请求,所以需要一定的优化方式,否则爬取速度会很缓慢。
思路
笔趣阁作为一个服务端渲染的网站,所以使用axios
+cheerio
也能非常简单的爬取了。
- 找到目标小说的
url
,解析该小说的书名和目录(目录有对应章节的链接); - 遍历小说目录,通过请求对应的
path
,来解析出文章内容; - 把解析的内容通过
fs
模块写到文件里。
有了一个简单的思路,就可以开始编码实现了。
实现
创建一个
index.js
文件,并通过npm init -y
初始化,然后下载我们要用的依赖:1
npm install --save axios cheerio
请求目标小说,获取内容:
1
2
3
4
5
6
7
8
9
10const Axios = require('axios')
const cheerio = require('cheerio')
const baseURL = 'https://www.xbiquge.la/' // 笔趣阁的网站
const outDir = 'dist' // 写入的文件目录
const targetURL = '7/7877/' // 通过网站url的规律,找到对应的书名pathname
const axios = Axios.create({
timeout: 0
})
const res = await axios.get(baseURL + targetURL)
console.log(res.data)
从上图,可以看到,通过axios
直接请求地址后,会获得返回的HTML
内容,有了这个内容后,就可以通过cheerio
来获取任意的内容。
- 分析节点信息,使用
cheerio
获取需要的信息:
找到有用的节点信息后,开始编码:
1 | const res = await axios.get(baseURL + targetURL) |
从上面可以看到cheerio
的API
和jQuery
基本一样方便,因此也很容易的获取到了目录的信息。有了目录的章节信息后,就可以依葫芦画瓢,遍历请求抓取对应内容了。
现在,能抓取到内容后,就可以考虑下一步:把抓取的文章内容保存到本。
保存文章内容到本地文件:
在NodeJS
中,可以通过 fs 模块来读写文件,使用起来非常的容易。1
2
3
4
5
6
7
8
9const ws = fs.createWriteStream(resolve(outDir, `${title}.txt`), {
flags: 'a' // 表示写入方式为追加
})
for (const b of list) {
const content = '...' // 通过cheerio分别获取内容
ws.write(title + '\n') // \n换行符
ws.write(content + '\n')
}
ws.end()
正常情况下,就可以看到dist
目录下生成的对应书名的.txt
文件了,这样小说就算简单的爬取下来了。
优化
虽然实现了下载小说的目的,但发现了有几个比较严重的问题所在:
- 小说章节太多,下载缓慢;
- 请求过于频繁,有些请求会失败,造成内容不全;
- 长时间的程序执行,没有反馈交互,显得程序像卡死。
发现问题所在后,就得想办法来解决,思考之后想出以下方式:
- 通过
setTimeout
来开启异步,加快下载速率; - 使用
try/catch
配合递归的方式,一直等请求成功(可以设置一个重试次数); - 使用
cli-progress
来反馈下载进度。
分片下载
通过setTimeout
来异步下载小说,需要考虑顺序问题。这里定义一个step
来表示分成几段来下载,最后来把片段给接上:
1 | async function downloadBook (title, book, p = 5) { |
以上就是分段来异步下载的一个简单实现,接着看怎么处理异常:
异常处理
请求的次数多了后,肯定会出现丢包的现象,从而导致内容不全,这种时候就需要做对应处理了:
1 | async function getHTMLContent (url, selectors, title = '') { |
当因为网络失败后,会try
捕获走catch
方法,在catch
方法里我们递归调用,从新再次请求这次失败的章节,保证内容不会丢失。
进度条反馈
使用进度条的话,需要下载 cli-progress 库:
1 | npm install --save cli-progress |
使用方式非常的简单:
1 | const bar = new cliProgress.SingleBar({ |
其中的几个参数表达意思为:
format
: 表示格式化后的显示内容;barIncompleteChar
: 未下载的显示字符;start
: 初始化进度条,第一个参数是总是,第二个是完成数:一般为0;increment
: 完成数自增1.
完成
优化完成后,就可以看看现在的效果了:
当下载完成之后,就会把这几个文件归档为一个斗破苍穹.txt
的文件。
以上,就是怎样使用NodeJS抓取一个普通网站内容的思路了,谢谢阅读Thanks♪(・ω・)ノ。
评论区
欢迎你留下宝贵的意见,昵称输入QQ号会显示QQ头像哦~