最近專案需要一些資訊,因為專案是用
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下來看就好了