使用 Node.js 开发资讯爬虫流程
最近项目需要一些资讯,因为项目是用 node.js 来写的,所以就自然地用 node.js 来写爬虫了
项目地址:github.com/mrtanweijie… ,项目里面爬取了 readhub 、 开源中国 、 开发者头条 、 36kr 这几个网站的资讯内容,暂时没有对多页面进行处理,因为每天爬虫都会跑一次,现在每次获取到最新的就可以满足需求了,后期再进行完善
爬虫流程概括下来就是把目标网站的html下载到本地再进行数据提取。
一、下载页面
node.js 有很多http请求库,这里使用 request ,主要代码如下:
requestdownloadhtml () { const options = { url: this.url, headers: { 'user-agent': this.randomuseragent() } } return new promise((resolve, reject) => { request(options, (err, response, body) => { if (!err && response.statuscode === 200) { return resolve(body) } else { return reject(err) } }) }) }
使用 promise 来进行包装,便于后面使用的时候用上 async/await 。因为有很多网站是在客户端渲染的,所以下载到的页面不一定包含想要的html内容,我们可以使用 google 的 puppeteer 来下载客户端渲染的网站页面。众所周知的原因,在 npm i 的时候 puppeteer 可能因为需要下载chrome内核导致安装会失败,多试几次就好了:)
puppeteerdownloadhtml () { return new promise(async (resolve, reject) => { try { const browser = await puppeteer.launch({ headless: true }) const page = await browser.newpage() await page.goto(this.url) const bodyhandle = await page.$('body') const bodyhtml = await page.evaluate(body => body.innerhtml, bodyhandle) return resolve(bodyhtml) } catch (err) { console.log(err) return reject(err) } }) }
当然客户端渲染的页面最好是直接使用接口请求的方式,这样后面的html解析都不需要了,进行一下简单的封装,然后就可以像这样使用了: #滑稽 :)
await new downloader('http://36kr.com/newsflashes', downloader.puppeteer).downloadhtml()
二、html内容提取
html内容提取当然是使用神器 cheerio 了, cheerio 暴露了和 jquery 一样的接口,用起来非常简单。浏览器打开页面 f12 查看提取的页面元素节点,然后根据需求来提取内容即可
readhubextract () { let nodelist = this.$('#itemlist').find('.enablevisited') nodelist.each((i, e) => { let a = this.$(e).find('a') this.extractdata.push( this.extractdatafactory( a.attr('href'), a.text(), '', sourcecode.readhub ) ) }) return this.extractdata }
三、定时任务
cron 每天跑一跑 function job () { let cronjob = new cron.cronjob({ crontime: cronconfig.crontime, ontick: () => { spider() }, start: false }) cronjob.start() }
四、数据持久化
数据持久化理论上应该不属于爬虫关心的范围,用 mongoose ,创建model
import mongoose from 'mongoose' const schema = mongoose.schema const newsschema = new schema( { title: { type: 'string', required: true }, url: { type: 'string', required: true }, summary: string, recommend: { type: boolean, default: false }, source: { type: number, required: true, default: 0 }, status: { type: number, required: true, default: 0 }, createdtime: { type: date, default: date.now } }, { collection: 'news' } ) export default mongoose.model('news', newsschema)
基本操作
import { obj_status } from '../../constants' class baseservice { constructor (objmodel) { this.objmodel = objmodel } saveobject (objdata) { return new promise((resolve, reject) => { this.objmodel(objdata).save((err, result) => { if (err) { return reject(err) } return resolve(result) }) }) } } export default baseservice
资讯
import baseservice from './baseservice' import news from '../models/news' class newsservice extends baseservice {} export default new newsservice(news)
愉快地保存数据
await newsservice.batchsave(newslisttem)
更多内容到github把项目clone下来看就好了。
总结