若是隻寫怎麼抓取網頁,確定會被吐槽太水,知足不了讀者的逼格要求,因此本文會經過不斷的審視代碼,作到令本身滿意(擼碼也要不斷迸發新想法!html
本文目標:抓取什麼值得買網站國內優惠的最新商品,而且做爲對象輸出出來,方便後續入庫等操做git
本文就介紹兩個:request
和 cheerio
,另外lodash
是個工具庫,不作介紹,後面篇幅會繼續介紹其餘用到的npm庫。github
var request = require('request');
request('http://www.smzdm.com/youhui/', (err, req)=>{
if(!err){
console.log(Object.keys(req))
}
})複製代碼
經過上面的代碼就看到req
實際是個response
對象,包括headers
、statusCode
、body
等,咱們用body
就是網站的html內容npm
var request = require('request')
var cheerio = require('cheerio')
cheerio.prototype.removeTagText = function () {
var html = this.html()
return html.replace(/<([\w\d]+)\b[^<]+?<\/\1>/g, (m) => {
return ''
})
}
request('http://www.smzdm.com/youhui/', (err, req) => {
if (!err) {
var body = req.body
var $ = cheerio.load(body, {
decodeEntities: false
})
$('.list.list_preferential').each((i, item) => {
var $title = $('.itemName a', item)
var url = $title.attr('href')
var title = $title.removeTagText().trim()
var hl = $title.children().text().trim()
var img = $('img', item).attr('src')
var desc = $('.lrInfo', item).html().trim()
desc = desc.replace(/<a\b.+?>閱讀全文<\/a>/g, '')
var mall = $('.botPart a.mall', item).text().trim()
console.log({title, hl, url, img, desc, mall})
})
}
})複製代碼
簡單解釋下,removeTagText
是直接擴展了cheerio
的一個方法,目的是去掉相似json
再特價:QuanU 全友 布藝沙發組合<span class="z-highlight">2798元包郵(需定金99元,3.1付尾款)</span>複製代碼
裏面span
以後的文字。執行後獲得下面的結果:promise
從上面需求來看,只須要提取列表頁面的商品信息,而取到數據以後,使用cheerio
進行了解析,而後經過一些「選擇器」對數據進行「提取加工」,獲得想要的數據。數據結構
重點是選擇器 和 提取加工,若是想要的字段多了,那麼代碼會越寫越多,維護困難,最重要的是「不環保」,今天抓什麼值得買,明天抓惠惠網,代碼還要copy一份改一改!一來二去,抓的越多,那麼代碼越亂,想一想哪天不用request
了,是否是要挨個修改呢?因此要抓重點,從最後須要的數據結構入手,關注選擇器 和 提取加工。ide
從最後須要的數據結構入手,關注選擇器 和 提取加工。我設計一種對象結構,做爲參數傳入,這個參數我起名:handlerMap
,最後實現一個spider
的函數,用法以下:函數
spider(url, callback, handlerMap)複製代碼
從目標數據結構出發,最後數據什麼樣子,那麼handlerMap
結構就是什麼樣子,key
就是最後輸出數據的key
,是由selector
和handler
兩個key組成的對象,相似咱們須要最後產出的數據是:工具
[{
title: '',
ht: '',
url: '',
img: '',
mall: '',
desc: ''
}, {item2..}...]複製代碼
那麼須要的handlerMap
就是:
{
title: {
selector: '.itemName a',
handler: 'removeTagText'
},
ht: {
selector: '.itemName a span',
handler: 'text'
},
url: {
selector: '.itemName a',
handler: 'atrr:href'
},
img: {
selector: 'img',
handler: 'attr:src'
},
mall: {
selector: '.botPart a.mall',
handler: 'text'
},
desc: {
selector: '.lrInfo',
handler: function (data){
return data.replace(/<a\b.+?>閱讀全文<\/a>/g, '')
}
}
}複製代碼
再酷一點,就是簡寫方法:url:".itemName a!attr:href」
,另外再加上若是抓取的是JSON
數據,也要一塊兒處理的狀況。通過分析以後,開始改造代碼,代碼最後分爲了兩個模塊:
spider.js
:包裝request 模塊,負責抓取頁面將頁面交給parser.js
解析出來想要的數據parser.js
:負責解析handlerMap,同時支持json和html兩種類型的頁面進行解析雖然增長很多代碼工做量,可是抽象後的代碼在使用的時候就更加方便了,本身仍是別人在使用的時候,不用關心代碼實現,只須要關注抓取的頁面url、要提取的頁面內容和數據獲得後的繼續操做便可,使用起來要比以前混雜在一塊兒的代碼更加清晰簡潔;而且抓取任意頁面都不須要動核心的代碼,只須要填寫前面提到的handlerMap
。
其實Node抓取頁面很簡單,本文只是經過一個簡單的抓取任務,不斷深刻思考,進行抽象,寫出本身滿意的代碼,以小見大,但願本文對讀者有所啓發😄
今天到此結束,完成一個基礎抓取的庫,有空繼續介紹Node抓站的知識,歡迎你們交流討論
本文的完整代碼,在github/ksky521/mpdemo/ 對應文章名文件夾下能夠找到
-eof-
@三水清
未經容許,請勿轉載,不用打賞,喜歡請轉發和關注
感受有用,歡迎關注個人公衆號,每週一篇原創技術文章