如果只寫怎麼抓取網頁,肯定會被吐槽太水,滿足不了讀者的逼格要求,所以本文會通過不斷的審視程式碼,做到令自己滿意(擼碼也要不斷迸發新想法!
本文目標:抓取什麼值得買網站國內優惠的最新商品,並且作為物件輸出出來,方便後續入庫等操作
抓取常用到的npm模組
本文就介紹兩個:request
和 cheerio
,另外lodash
是個工具庫,不做介紹,後面篇幅會繼續介紹其他用到的npm庫。
- request:是一個http請求庫,封裝了很多常用的配置,而且也有promise版本(還有next版本。
- cheerio:是一個類似jQuery的庫,可以將html String轉成類似jQ的物件,增加jQ的操作方法(實際是htmlparser2
request 示例
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內容
cheerio 示例
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
的一個方法,目的是去掉類似
再特價:QuanU 全友 布藝沙發組合<span class="z-highlight">2798元包郵(需定金99元,3.1付尾款)</span>複製程式碼
裡面span
之後的文字。執行後得到下面的結果:
怎麼寫出自己滿意的程式碼
從上面需求來看,只需要提取列表頁面的商品資訊,而取到資料之後,使用cheerio
進行了解析,然後通過一些「選擇器」對資料進行「提取加工」,得到想要的資料。
重點是選擇器 和 提取加工,如果想要的欄位多了,那麼程式碼會越寫越多,維護困難,最重要的是「不環保」,今天抓什麼值得買,明天抓惠惠網,程式碼還要copy一份改一改!一來二去,抓的越多,那麼程式碼越亂,想想哪天不用request
了,是不是要挨個修改呢?所以要抓重點,從最後需要的資料結構入手,關注選擇器 和 提取加工。
handlerMap
從最後需要的資料結構入手,關注選擇器 和 提取加工。我設計一種物件結構,作為引數傳入,這個引數我起名: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-
@三水清
未經允許,請勿轉載,不用打賞,喜歡請轉發和關注
感覺有用,歡迎關注我的公眾號,每週一篇原創技術文章