您好,登錄后才能下訂單哦!
小編給大家分享一下如何使用node.js爬取指定排名網(wǎng)站的JS引用庫(kù),相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
所用到的node主要模塊
express 不用多說(shuō)
request http模塊
cheerio 運(yùn)行在服務(wù)器端的jQuery
node-inspector node調(diào)試模塊
node-dev 修改文件后自動(dòng)重啟app
關(guān)于調(diào)試Node
在任意一個(gè)文件夾,執(zhí)行node-inspector,通過(guò)打開(kāi)特定頁(yè)面,在頁(yè)面上進(jìn)行調(diào)試,然后運(yùn)行app,使用node-dev app.js來(lái)自動(dòng)重啟應(yīng)用。
所碰到的問(wèn)題
1. request請(qǐng)求多個(gè)頁(yè)面
由于請(qǐng)求是異步執(zhí)行的,和分別返回3個(gè)頁(yè)面的數(shù)據(jù),這里只爬取了50個(gè)網(wǎng)站,一個(gè)頁(yè)面有20個(gè),所以有3頁(yè),通過(guò)循環(huán)里套request請(qǐng)求,來(lái)實(shí)現(xiàn)。
通過(guò)添加請(qǐng)求頭可以實(shí)現(xiàn)基本的反爬蟲
處理數(shù)據(jù)的方法都寫在analyData()
里面,造成后面的數(shù)據(jù)重復(fù)存儲(chǔ)了,想了很久,才想到一個(gè)解決方法,后面會(huì)寫到是怎么解決的。
for (var i = 1; i < len+1; i++) { (function(i){ var options = { url: 'http://www.alexa.cn/siterank/' + i, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36' } }; request(options, function (err, response, body) { analyData(body,rank); }) })(i) }
2. 多層回調(diào)
仔細(xì)觀察代碼,你會(huì)發(fā)現(xiàn),處理數(shù)據(jù)的方法使用了如下的多層回調(diào),也可以不使用回調(diào),寫在一個(gè)函數(shù)內(nèi)部;因?yàn)?,每層都要使用上一層的?shù)據(jù),造成了這樣的寫法。
function f1(data1){ f2(data1); } function f2(data2){ f3(data2); } function f3(data3){ f4(data4); }
3. 正則獲取JS庫(kù)
由于獲取頁(yè)面庫(kù),首先需要獲取到script的src屬性,然后通過(guò)正則來(lái)實(shí)現(xiàn)字符串匹配。
<script src="https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/lib/jquery-1.10.2_d88366fd.js"></script>
獲取到的script可能是上面這樣的,由于庫(kù)名的命名真是各種各樣,后來(lái)想了一下,因?yàn)槲募怯?js結(jié)尾的,所以就以點(diǎn)號(hào)為結(jié)尾,然后把點(diǎn)號(hào)之前的字符截取下來(lái),這樣獲得了庫(kù)名,代碼如下。
var reg = /[^\/\\]+$/g; var libName = jsLink.match(reg).join(''); var libFilter = libName.slice(0,libName.indexOf('.'));
4.cheerio模塊獲取JS引用鏈接
這部分也花了一點(diǎn)時(shí)間,才搞定,cheerio獲取DOM的方法和jQuery是一樣的,需要對(duì)返回的DOM對(duì)象進(jìn)行查看,就可以看到對(duì)象里隱藏好深的href屬性,方法大同小異,你也可以使用其他選擇器,選擇到script標(biāo)簽
var $ = cheerio.load(body); var scriptFile = $('script').toArray(); scriptFile.forEach(function(item,index){ if (item.attribs.src != null) { obtainLibName(item.attribs.src,index); }
5.存儲(chǔ)數(shù)據(jù)到數(shù)據(jù)庫(kù)
存儲(chǔ)數(shù)據(jù)的邏輯是先獲取所有的script信息,然后push到一個(gè)緩存數(shù)組,由于push后面,緊跟著存儲(chǔ)到數(shù)據(jù)庫(kù)的方法,這兩個(gè)方法都寫在循環(huán)里面的,例如爬取5個(gè)網(wǎng)站,每個(gè)網(wǎng)站存儲(chǔ)一次,后面也會(huì)跟著存儲(chǔ),造成數(shù)據(jù)重復(fù)存儲(chǔ)。解決方法是存儲(chǔ)數(shù)據(jù)的一般邏輯是先查,再存,這個(gè)查比較重要,查詢的方法也有多種,這里主要是根據(jù)庫(kù)名來(lái)查找唯一的數(shù)據(jù)對(duì)象,使用findOne方法。注意,由于node.js是異步執(zhí)行的,這里的閉包,每次只傳一個(gè)i值進(jìn)去,執(zhí)行存儲(chǔ)的操作。
// 將緩存數(shù)據(jù)存儲(chǔ)到數(shù)據(jù)庫(kù) function store2db(libObj){ console.log(libObj); for (var i = 0; i < libObj.length; i++) { (function(i){ var jsLib = new JsLib({ name: libObj[i].lib, libsNum: libObj[i].num }); JsLib.findOne({'name': libObj[i].lib},function(err,libDoc){ if(err) console.log(err); // console.log(libDoc) if (!libDoc){ jsLib.save(function(err,result){ if(err) console.log('保存數(shù)據(jù)出錯(cuò)' + err); }); } }) })(i) } console.log('一共存儲(chǔ)' + libObj.length + '條數(shù)據(jù)到數(shù)據(jù)庫(kù)'); }
6.分頁(yè)插件
本爬蟲前端使用了bootstrap.paginator插件,主要是前臺(tái)分頁(yè),返回?cái)?shù)據(jù),根據(jù)點(diǎn)擊的頁(yè)數(shù),來(lái)顯示對(duì)應(yīng)的數(shù)據(jù),后期考慮使用AJAX請(qǐng)求的方式來(lái)實(shí)現(xiàn)翻頁(yè)的效果,這里的注意項(xiàng),主要是最后一頁(yè)的顯示,最好前面做個(gè)判斷,因?yàn)榉祷氐臄?shù)據(jù),不一定剛好是頁(yè)數(shù)的整數(shù)倍
function _paging(libObj) { var ele = $('#page'); var pages = Math.ceil(libObj.length/20); console.log('總頁(yè)數(shù)' + pages); ele.bootstrapPaginator({ currentPage: 1, totalPages: pages, size:"normal", bootstrapMajorVersion: 3, alignment:"left", numberOfPages:pages, itemTexts: function (type, page, current) { switch (type) { case "first": return "首頁(yè)"; case "prev": return "上一頁(yè)"; case "next": return "下一頁(yè)"; case "last": return "末頁(yè)"; case "page": return page; } }, onPageClicked: function(event, originalEvent, type, page){ // console.log('當(dāng)前選中第:' + page + '頁(yè)'); var pHtml = ''; var endPage; var startPage = (page-1) * 20; if (page < pages) { endPage = page * 20; }else{ endPage = libObj.length; } for (var i = startPage; i < endPage; i++) { pHtml += '<tr><td>'; pHtml += (i+1) + '</td><td>'; pHtml += libObj[i].name + '</td><td>'; pHtml += libObj[i].libsNum + '</td></tr>'; } libShow.html(pHtml); } }) }
完整代碼
1. 前端
$(function () { var query = $('.query'), rank = $('.rank'), show = $('.show'), queryLib = $('.queryLib'), libShow = $('#libShow'), libName = $('.libName'), displayResult = $('.displayResult'); var checkLib = (function(){ function _query(){ query.click(function(){ $.post( '/query', { rank: rank.val(), }, function(data){ console.log(data); } ) }); queryLib.click(function(){ var inputLibName = libName.val(); if (inputLibName.length == 0) { alert('請(qǐng)輸入庫(kù)名~'); return; } $.post( '/queryLib', { libName: inputLibName, }, function(data){ if(data.length == 0){ alert('沒(méi)有查詢到名為' + inputLibName + '的庫(kù)'); libName.val(''); libName.focus(); libShow.html('') return; } var libHtml = ''; for (var i = 0; i < data.length; i++) { libHtml += '<tr><td>'; libHtml += (i+1) + '</td><td>'; libHtml += data[i].name + '</td><td>'; libHtml += data[i].libsNum + '</td></tr>'; } libShow.html(libHtml); } ) }); } function _showLibs(){ show.click(function(){ $.get( '/getLibs', { rank: rank.val(), }, function(data){ console.log('一共返回'+ data.length + '條數(shù)據(jù)'); console.log(data) var libHtml = ''; for (var i = 0; i < 20; i++) { libHtml += '<tr><td>'; libHtml += (i+1) + '</td><td>'; libHtml += data[i].name + '</td><td>'; libHtml += data[i].libsNum + '</td></tr>'; } displayResult.show(); libShow.html(libHtml);// 點(diǎn)擊顯示按鈕,顯示前20項(xiàng)數(shù)據(jù) _paging(data); } ) }); } //翻頁(yè)器 function _paging(libObj) { var ele = $('#page'); var pages = Math.ceil(libObj.length/20); console.log('總頁(yè)數(shù)' + pages); ele.bootstrapPaginator({ currentPage: 1, totalPages: pages, size:"normal", bootstrapMajorVersion: 3, alignment:"left", numberOfPages:pages, itemTexts: function (type, page, current) { switch (type) { case "first": return "首頁(yè)"; case "prev": return "上一頁(yè)"; case "next": return "下一頁(yè)"; case "last": return "末頁(yè)"; case "page": return page; } }, onPageClicked: function(event, originalEvent, type, page){ // console.log('當(dāng)前選中第:' + page + '頁(yè)'); var pHtml = ''; var endPage; var startPage = (page-1) * 20; if (page < pages) { endPage = page * 20; }else{ endPage = libObj.length; } for (var i = startPage; i < endPage; i++) { pHtml += '<tr><td>'; pHtml += (i+1) + '</td><td>'; pHtml += libObj[i].name + '</td><td>'; pHtml += libObj[i].libsNum + '</td></tr>'; } libShow.html(pHtml); } }) } function init() { _query(); _showLibs(); } return { init: init } })(); checkLib.init(); })
2.后端路由
var express = require('express'); var mongoose = require('mongoose'); var request = require('request'); var cheerio =require('cheerio'); var router = express.Router(); var JsLib = require('../model/jsLib') /* 顯示主頁(yè) */ router.get('/', function(req, res, next) { res.render('index'); }); // 顯示庫(kù) router.get('/getLibs',function(req,res,next){ JsLib.find({}) .sort({'libsNum': -1}) .exec(function(err,data){ res.json(data); }) }) // 庫(kù)的查詢 router.post('/queryLib',function(req,res,next){ var libName = req.body.libName; JsLib.find({ name: libName }).exec(function(err,data){ if (err) console.log('查詢出現(xiàn)錯(cuò)誤' + err); res.json(data); }) }) router.post('/query',function(req,res,next) { var rank = req.body.rank; var len = Math.round(rank/20); for (var i = 1; i < len+1; i++) { (function(i){ var options = { url: 'http://www.alexa.cn/siterank/' + i, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36' } }; request(options, function (err, response, body) { analyData(body,rank); }) })(i) } res.json('保存成功') }) var sites = []; var flag = 0; function analyData(data,rank) { if(data.indexOf('html') == -1) return false; var $ = cheerio.load(data);// 傳遞 HTML var sitesArr = $('.info-wrap .domain-link a').toArray();//將所有a鏈接存為數(shù)組 console.log('網(wǎng)站爬取中``') for (var i = 0; i < 10; i++) { // ***這里后面要改,默認(rèn)爬取前10名 var url = sitesArr[i].attribs.href; sites.push(url);//保存網(wǎng)址,添加wwww前綴 } console.log(sites); console.log('一共爬取' + sites.length +'個(gè)網(wǎng)站'); console.log('存儲(chǔ)數(shù)據(jù)中...') getScript(sites); } // 獲取JS庫(kù)文件地址 function getScript(urls) { var scriptArr = []; var src = []; var jsSrc = []; for (var j = 0; j < urls.length; j++) { (function(i,callback){ var options = { url: urls[i], headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36' } } request(options, function (err, res, body) { if(err) console.log('出現(xiàn)錯(cuò)誤: '+err); var $ = cheerio.load(body); var scriptFile = $('script').toArray(); callback(scriptFile,options.url); }) })(j,storeLib) }; function storeLib(scriptFile,url){ flag++;// 是否存儲(chǔ)數(shù)據(jù)的標(biāo)志 scriptFile.forEach(function(item,index){ if (item.attribs.src != null) { obtainLibName(item.attribs.src,index); } }) function obtainLibName(jsLink,i){ var reg = /[^\/\\]+$/g; var libName = jsLink.match(reg).join(''); var libFilter = libName.slice(0,libName.indexOf('.')); src.push(libFilter); } // console.log(src.length); // console.log(calcNum(src).length) (function(len,urlLength,src){ // console.log('length is '+ len) if (len == 10 ) {// len長(zhǎng)度為url的長(zhǎng)度才向src和數(shù)據(jù)庫(kù)里存儲(chǔ)數(shù)據(jù),防止重復(fù)儲(chǔ)存 // calcNum(src);//存儲(chǔ)數(shù)據(jù)到數(shù)據(jù)庫(kù) // ***這里后面要改,默認(rèn)爬取前10名 var libSrc = calcNum(src); store2db(libSrc); } })(flag,urls.length,src) } }// getScript END // 將緩存數(shù)據(jù)存儲(chǔ)到數(shù)據(jù)庫(kù) function store2db(libObj){ console.log(libObj); for (var i = 0; i < libObj.length; i++) { (function(i){ var jsLib = new JsLib({ name: libObj[i].lib, libsNum: libObj[i].num }); JsLib.findOne({'name': libObj[i].lib},function(err,libDoc){ if(err) console.log(err); // console.log(libDoc) if (!libDoc){ jsLib.save(function(err,result){ if(err) console.log('保存數(shù)據(jù)出錯(cuò)' + err); }); } }) })(i) } console.log('一共存儲(chǔ)' + libObj.length + '條數(shù)據(jù)到數(shù)據(jù)庫(kù)'); } // JS庫(kù)排序算法 function calcNum(arr){ var libObj = {}; var result = []; for (var i = 0, len = arr.length; i < len; i++) { if (libObj[arr[i]]) { libObj[arr[i]] ++; } else { libObj[arr[i]] = 1; } } for(var o in libObj){ result.push({ lib: o, num: libObj[o] }) } result.sort(function(a,b){ return b.num - a.num; }); return result; } module.exports = router;
以上是“如何使用node.js爬取指定排名網(wǎng)站的JS引用庫(kù)”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。