node怎么爬取數(shù)據(jù)?下面本篇文章給大家分享一個(gè)node爬蟲(chóng)實(shí)例,聊聊利用node抓取小說(shuō)章節(jié)的方法,希望對(duì)大家有所幫助!
準(zhǔn)備用electron
制作一個(gè)小說(shuō)閱讀工具練練手,那么首先要解決的就是數(shù)據(jù)問(wèn)題,也就是小說(shuō)的文本。
這里準(zhǔn)備使用nodejs對(duì)小說(shuō)網(wǎng)站進(jìn)行爬蟲(chóng)爬取,嘗試爬下一本小說(shuō),數(shù)據(jù)就不存放數(shù)據(jù)庫(kù)了,先使用txt
作為文本存儲(chǔ)
在node
中對(duì)于網(wǎng)站的請(qǐng)求,本身就存在http
和https
庫(kù),內(nèi)部含有request
請(qǐng)求方法。
實(shí)例:
request = https.request(TestUrl, { encoding:'utf-8' }, (res)=>{ let chunks = '' res.on('data', (chunk)=>{ chunks += chunk }) res.on('end',function(){ console.log('請(qǐng)求結(jié)束'); }) })
但是也就到此為止了,只是存取了一個(gè)html
的文本數(shù)據(jù),并不能夠?qū)?nèi)部元素進(jìn)行提取之類的工作(也可以正則拿,但是太過(guò)復(fù)雜)。
我將訪問(wèn)到的數(shù)據(jù)通過(guò)fs.writeFile
方法存儲(chǔ)起來(lái)了,這只是整個(gè)網(wǎng)頁(yè)的html
但是我想要的還有各個(gè)章節(jié)中的內(nèi)容,這樣一來(lái)就需要獲取章節(jié)的超鏈接,組成超鏈接鏈表進(jìn)去爬取
cheerio庫(kù)
所以,這里就要介紹一個(gè)js的庫(kù)了,cheerio
官方文檔:https://cheerio.js.org/
中文文檔:https://github.com/cheeriojs/cheerio/wiki/Chinese-README
在文檔中,可以使用示例進(jìn)行調(diào)試
使用cheerio解析HTML
cheerio解析html時(shí),獲取dom節(jié)點(diǎn)的方式與jquery
相似。
根據(jù)之前獲取到的書(shū)籍首頁(yè)的html,查找自己想要的dom節(jié)點(diǎn)數(shù)據(jù)
const fs = require('fs') const cheerio = require('cheerio'); // 引入讀取方法 const { getFile, writeFun } = require('./requestNovel') let hasIndexPromise = getFile('./hasGetfile/index.html'); let bookArray = []; hasIndexPromise.then((res)=>{ let htmlstr = res; let $ = cheerio.load(htmlstr); $(".listmain dl dd a").map((index, item)=>{ let name = $(item).text(), href = 'https://www.shuquge.com/txt/147032/' + $(item).attr('href') if (index > 11){ bookArray.push({ name, href }) } }) // console.log(bookArray) writeFun('./hasGetfile/hrefList.txt', JSON.stringify(bookArray), 'w') })
打印一下信息
可以同時(shí)將這些信息也存儲(chǔ)起來(lái)
現(xiàn)在章節(jié)數(shù)和章節(jié)的鏈接都有了,那么就可以獲取章節(jié)的內(nèi)容了。
因?yàn)榕颗廊∽詈笮枰狪P代理,這里還沒(méi)準(zhǔn)備,暫時(shí)先寫(xiě)獲取某一章節(jié)小說(shuō)的內(nèi)容方法
爬取某一章節(jié)的內(nèi)容其實(shí)也比較簡(jiǎn)單:
// 爬取某一章節(jié)的內(nèi)容方法 function getOneChapter(n) { return new Promise((resolve, reject)=>{ if (n >= bookArray.length) { reject('未能找到') } let name = bookArray[n].name; request = https.request(bookArray[n].href, { encoding:'gbk' }, (res)=>{ let html = '' res.on('data', chunk=>{ html += chunk; }) res.on('end', ()=>{ let $ = cheerio.load(html); let content = $("#content").text(); if (content) { // 寫(xiě)成txt writeFun(`./hasGetfile/${name}.txt`, content, 'w') resolve(content); } else { reject('未能找到') } }) }) request.end(); }) } getOneChapter(10)
這樣,就可以根據(jù)上面的方法,來(lái)創(chuàng)造一個(gè)調(diào)用接口,傳入不同的章節(jié)參數(shù),獲取當(dāng)前章節(jié)的數(shù)據(jù)
const express = require('express'); const IO = express(); const { getAllChapter, getOneChapter } = require('./readIndex') // 獲取章節(jié)超鏈接鏈表 getAllChapter(); IO.use('/book',function(req, res) { // 參數(shù) let query = req.query; if (query.n) { // 獲取某一章節(jié)數(shù)據(jù) let promise = getOneChapter(parseInt(query.n - 1)); promise.then((d)=>{ res.json({ d: d }) }, (d)=>{ res.json({ d: d }) }) } else { res.json({ d: 404 }) } }) //服務(wù)器本地主機(jī)的數(shù)字 IO.listen('7001',function(){ console.log("啟動(dòng)了。。。"); })
效果:
現(xiàn)在,一個(gè)簡(jiǎn)單的查找章節(jié)接口就做好了,并且也可以做一些參數(shù)超出判斷。
對(duì)于不同的數(shù)據(jù)接口,爬蟲(chóng)處理方式也不一樣,不過(guò)在本次爬取的鏈接中,內(nèi)容的顯示并不是由前端動(dòng)態(tài)渲染出來(lái)的,所以可以直接爬取靜態(tài)的html即可。如果遇到數(shù)據(jù)是通過(guò)Ajax之類的方式獲取到的json串,那就要通過(guò)網(wǎng)絡(luò)接口去請(qǐng)求數(shù)據(jù)了。