使用 Node.js 來開發一個資訊爬蟲

最近項目須要一些資訊,由於項目是用Node.js來寫的,因此就天然地用Node.js來寫爬蟲了node

項目地址:github.com/mrtanweijie… ,項目裏面爬取了Readhub開源中國開發者頭條36Kr這幾個網站的資訊內容,暫時沒有對多頁面進行處理,由於天天爬蟲都會跑一次,如今每次獲取到最新的就能夠知足需求了,後期再進行完善git

爬蟲流程歸納下來就是把目標網站的HTML下載到本地再進行數據提取。github

1、下載頁面

Node.js有不少http請求庫,這裏使用request,主要代碼以下:npm

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內容,咱們可使用Googlepuppeteer來下載客戶端渲染的網站頁面。衆所周知的緣由,在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解析都不須要了,進行一下簡單的封裝,而後就能夠像這樣使用了: #滑稽 :)bash

await new Downloader('http://36kr.com/newsflashes', DOWNLOADER.puppeteer).downloadHTML()
複製代碼

2、HTML內容提取

HTML內容提取固然是使用神器cheerio了,cheerio暴露了和jQuery同樣的接口,用起來很是簡單。瀏覽器打開頁面F12查看提取的頁面元素節點,而後根據需求來提取內容便可less

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
  }
複製代碼

3、定時任務

cron天天跑一跑dom

function job () {
  let cronJob = new cron.CronJob({
    cronTime: cronConfig.cronTime,
    onTick: () => {
      spider()
    },
    start: false
  })
  cronJob.start()
}
複製代碼

4、數據持久化

數據持久化理論上應該不屬於爬蟲關心的範圍,用mongoose,建立Modelasync

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)

複製代碼

基本操做mongoose

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下來看就行了

相關文章
相關標籤/搜索