最近項目需要一些資訊,因為項目是用 Node.js 來(lái)寫(xiě)的,所以就自然地用 Node.js 來(lái)寫(xiě)爬蟲(chóng)了
項目地址:github.com/mrtanweijie… ,項目里面爬取了 Readhub 、 開(kāi)源中國 、 開(kāi)發(fā)者頭條 、 36Kr 這幾個(gè)網(wǎng)站的資訊內容,暫時(shí)沒(méi)有對多頁(yè)面進(jìn)行處理,因為每天爬蟲(chóng)都會(huì )跑一次,現在每次獲取到最新的就可以滿(mǎn)足需求了,后期再進(jìn)行完善
爬蟲(chóng)流程概括下來(lái)就是把目標網(wǎng)站的HTML下載到本地再進(jìn)行數據提取。
一、下載頁(yè)面
Node.js 有很多http請求庫,這里使用 request ,主要代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | requestDownloadHTML () { const options = {  url: this.url,  headers: {  'User-Agent': this.randomUserAgent()  } } returnnewPromise((resolve, reject) => {  request(options, (err, response, body) => {  if(!err && response.statusCode === 200) {   returnresolve(body)  } else{   returnreject(err)  }  }) }) } | 
使用 Promise 來(lái)進(jìn)行包裝,便于后面使用的時(shí)候用上 async/await 。因為有很多網(wǎng)站是在客戶(hù)端渲染的,所以下載到的頁(yè)面不一定包含想要的HTML內容,我們可以使用 Google 的 puppeteer 來(lái)下載客戶(hù)端渲染的網(wǎng)站頁(yè)面。眾所周知的原因,在 npm i 的時(shí)候 puppeteer 可能因為需要下載Chrome內核導致安裝會(huì )失敗,多試幾次就好了:)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | puppeteerDownloadHTML () { returnnewPromise(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)  returnresolve(bodyHTML)  } catch(err) {  console.log(err)  returnreject(err)  } }) } | 
當然客戶(hù)端渲染的頁(yè)面最好是直接使用接口請求的方式,這樣后面的HTML解析都不需要了,進(jìn)行一下簡(jiǎn)單的封裝,然后就可以像這樣使用了: #滑稽 :)
| 1 |  | 
二、HTML內容提取
HTML內容提取當然是使用神器 cheerio 了, cheerio 暴露了和 jQuery 一樣的接口,用起來(lái)非常簡(jiǎn)單。瀏覽器打開(kāi)頁(yè)面 F12 查看提取的頁(yè)面元素節點(diǎn),然后根據需求來(lái)提取內容即可
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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  )  ) }) returnthis.extractData } | 
三、定時(shí)任務(wù)
| 1 2 3 4 5 6 7 8 9 10 11 | cron 每天跑一跑 functionjob () { let cronJob = newcron.CronJob({ cronTime: cronConfig.cronTime, onTick: () => {  spider() }, start: false }) cronJob.start()} | 
四、數據持久化
數據持久化理論上應該不屬于爬蟲(chóng)關(guān)心的范圍,用 mongoose ,創(chuàng )建Model
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import mongoose from 'mongoose'const Schema = mongoose.Schemaconst NewsSchema = newSchema( { 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 defaultmongoose.model('news', NewsSchema) | 
基本操作
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import { OBJ_STATUS } from '../../Constants'class BaseService { constructor (ObjModel) { this.ObjModel = ObjModel } saveObject (objData) { returnnewPromise((resolve, reject) => {  this.ObjModel(objData).save((err, result) => {  if(err) {   returnreject(err)  }  returnresolve(result)  }) }) }}export defaultBaseService | 
資訊
| 1 2 3 4 | import BaseService from './BaseService'import News from '../models/News'class NewsService extends BaseService {}export defaultnewNewsService(News) | 
愉快地保存數據
| 1 | await newsService.batchSave(newsListTem) | 
更多內容到Github把項目clone下來(lái)看就好了。
總結
原文鏈接:https://juejin.im/post/5a506e6d51882573450156e3?utm_source=tuicool&utm_medium=referral
 
           
          


 皖公網(wǎng)安備 34010202600669
皖公網(wǎng)安備 34010202600669



