欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

利用node.js爬取指定排名網(wǎng)站的JS引用庫詳解

 更新時(shí)間:2017年07月25日 11:45:12   作者:窗里窗外  
最近在學(xué)習(xí)node.js爬蟲,由于 nodejs 強(qiáng)大的異步特性,讓我們可以輕松以異步高并發(fā)去爬取網(wǎng)站,下面這篇文章主要給大家介紹了關(guān)于利用node.js爬取指定排名網(wǎng)站的JS引用庫的相關(guān)資料,需要的朋友可以參考下。

前言

本文給大家介紹的爬蟲將從網(wǎng)站爬取排名前幾的網(wǎng)站,具體前幾名可以具體設(shè)置,并分別爬取他們的主頁,檢查是否引用特定庫。下面話不多說了,來一起看看詳細(xì)的介紹:

所用到的node主要模塊

  • express 不用多說
  • 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,通過打開特定頁面,在頁面上進(jìn)行調(diào)試,然后運(yùn)行app,使用node-dev app.js來自動(dòng)重啟應(yīng)用。

所碰到的問題

1. request請求多個(gè)頁面

由于請求是異步執(zhí)行的,和分別返回3個(gè)頁面的數(shù)據(jù),這里只爬取了50個(gè)網(wǎng)站,一個(gè)頁面有20個(gè),所以有3頁,通過循環(huán)里套request請求,來實(shí)現(xiàn)。

通過添加請求頭可以實(shí)現(xiàn)基本的反爬蟲

處理數(shù)據(jù)的方法都寫在analyData()里面,造成后面的數(shù)據(jù)重復(fù)存儲了,想了很久,才想到一個(gè)解決方法,后面會寫到是怎么解決的。

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ì)觀察代碼,你會發(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庫

由于獲取頁面庫,首先需要獲取到script的src屬性,然后通過正則來實(shí)現(xiàn)字符串匹配。

<script src="https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/lib/jquery-1.10.2_d88366fd.js"></script>

獲取到的script可能是上面這樣的,由于庫名的命名真是各種各樣,后來想了一下,因?yàn)槲募怯?js結(jié)尾的,所以就以點(diǎn)號為結(jié)尾,然后把點(diǎn)號之前的字符截取下來,這樣獲得了庫名,代碼如下。

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是一樣的,需要對返回的DOM對象進(jìn)行查看,就可以看到對象里隱藏好深的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.存儲數(shù)據(jù)到數(shù)據(jù)庫

存儲數(shù)據(jù)的邏輯是先獲取所有的script信息,然后push到一個(gè)緩存數(shù)組,由于push后面,緊跟著存儲到數(shù)據(jù)庫的方法,這兩個(gè)方法都寫在循環(huán)里面的,例如爬取5個(gè)網(wǎng)站,每個(gè)網(wǎng)站存儲一次,后面也會跟著存儲,造成數(shù)據(jù)重復(fù)存儲。解決方法是存儲數(shù)據(jù)的一般邏輯是先查,再存,這個(gè)查比較重要,查詢的方法也有多種,這里主要是根據(jù)庫名來查找唯一的數(shù)據(jù)對象,使用findOne方法。注意,由于node.js是異步執(zhí)行的,這里的閉包,每次只傳一個(gè)i值進(jìn)去,執(zhí)行存儲的操作。

// 將緩存數(shù)據(jù)存儲到數(shù)據(jù)庫
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('一共存儲' + libObj.length + '條數(shù)據(jù)到數(shù)據(jù)庫');
}

6.分頁插件

本爬蟲前端使用了bootstrap.paginator插件,主要是前臺分頁,返回?cái)?shù)據(jù),根據(jù)點(diǎn)擊的頁數(shù),來顯示對應(yīng)的數(shù)據(jù),后期考慮使用AJAX請求的方式來實(shí)現(xiàn)翻頁的效果,這里的注意項(xiàng),主要是最后一頁的顯示,最好前面做個(gè)判斷,因?yàn)榉祷氐臄?shù)據(jù),不一定剛好是頁數(shù)的整數(shù)倍

function _paging(libObj) {
 var ele = $('#page');
 var pages = Math.ceil(libObj.length/20);
 console.log('總頁數(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 "首頁"; 
 case "prev": return "上一頁"; 
 case "next": return "下一頁"; 
 case "last": return "末頁"; 
 case "page": return page;
 }
 },
 onPageClicked: function(event, originalEvent, type, page){
 // console.log('當(dāng)前選中第:' + page + '頁');
 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('請輸入庫名~');
 return;
 }
 $.post(
 '/queryLib',
 {
 libName: inputLibName,
 },
 function(data){
 if(data.length == 0){
 alert('沒有查詢到名為' + inputLibName + '的庫');
 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);
 }
 )
 });
 }

 //翻頁器
 function _paging(libObj) {
 var ele = $('#page');
 var pages = Math.ceil(libObj.length/20);
 console.log('總頁數(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 "首頁"; 
 case "prev": return "上一頁"; 
 case "next": return "下一頁"; 
 case "last": return "末頁"; 
 case "page": return page;
 }
 },
 onPageClicked: function(event, originalEvent, type, page){
 // console.log('當(dāng)前選中第:' + page + '頁');
 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')

/* 顯示主頁 */
router.get('/', function(req, res, next) {
 res.render('index');
});

// 顯示庫
router.get('/getLibs',function(req,res,next){
 JsLib.find({})
 .sort({'libsNum': -1})
 .exec(function(err,data){
 res.json(data);
 })
})

// 庫的查詢
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('存儲數(shù)據(jù)中...')

 getScript(sites);
}


// 獲取JS庫文件地址
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++;// 是否存儲數(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長度為url的長度才向src和數(shù)據(jù)庫里存儲數(shù)據(jù),防止重復(fù)儲存
 // calcNum(src);//存儲數(shù)據(jù)到數(shù)據(jù)庫 // ***這里后面要改,默認(rèn)爬取前10名
 var libSrc = calcNum(src);
 store2db(libSrc);
 }
 })(flag,urls.length,src)
 } 
}// getScript END

// 將緩存數(shù)據(jù)存儲到數(shù)據(jù)庫
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('一共存儲' + libObj.length + '條數(shù)據(jù)到數(shù)據(jù)庫');
}
// JS庫排序算法
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;

源碼下載

github下載地址 (本地下載

后記

通過這個(gè)小爬蟲,學(xué)習(xí)到很多知識,例如爬蟲的反爬蟲有哪些策越,意識到node.js的異步執(zhí)行特性,前后端是怎么進(jìn)行交互的。同時(shí),也意識到有一些方面的不足,后面還需要繼續(xù)改進(jìn),歡迎大家的相互交流。

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Node.js開發(fā) path路徑模塊詳解

    Node.js開發(fā) path路徑模塊詳解

    path 模塊是 Node.js 官方提供的、用來處理路徑的模塊,它提供了一系列的方法和屬性,用來滿足用戶對路徑的處理需求,這篇文章主要介紹了Node.js開發(fā) path路徑模塊,需要的朋友可以參考下
    2024-02-02
  • node.js中的path.delimiter方法使用說明

    node.js中的path.delimiter方法使用說明

    這篇文章主要介紹了node.js中的path.delimiter方法使用說明,本文介紹了path.delimiter的方法說明、語法、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12
  • 解決npm管理員身份install時(shí)出現(xiàn)權(quán)限的問題

    解決npm管理員身份install時(shí)出現(xiàn)權(quán)限的問題

    下面小編就為大家分享一篇解決npm管理員身份install時(shí)出現(xiàn)權(quán)限的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-03-03
  • array-uniq的安裝和使用方法

    array-uniq的安裝和使用方法

    array-uniq是一個(gè)非常實(shí)用的NPM包,用于在JavaScript中創(chuàng)建不含重復(fù)元素的數(shù)組,它提供了一個(gè)簡單而有效的方法來去除數(shù)組中的重復(fù)項(xiàng),本文將介紹如何安裝和使用array-uniq來清理你的數(shù)組數(shù)據(jù),需要的朋友可以參考下
    2024-06-06
  • node.js實(shí)現(xiàn)微信開發(fā)之獲取用戶授權(quán)

    node.js實(shí)現(xiàn)微信開發(fā)之獲取用戶授權(quán)

    這篇文章主要介紹了node.js實(shí)現(xiàn)微信開發(fā)之獲取用戶授權(quán),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Node.js實(shí)現(xiàn)壓縮與解壓數(shù)據(jù)

    Node.js實(shí)現(xiàn)壓縮與解壓數(shù)據(jù)

    這篇文章介紹了Node.js實(shí)現(xiàn)壓縮與解壓數(shù)據(jù)的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • Node.js從字符串生成文件流的實(shí)現(xiàn)方法

    Node.js從字符串生成文件流的實(shí)現(xiàn)方法

    這篇文章主要介紹了Node.js從字符串生成文件流的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • 詳解nodejs微信公眾號開發(fā)——1.接入微信公眾號

    詳解nodejs微信公眾號開發(fā)——1.接入微信公眾號

    本篇文章主要介紹了詳解nodejs微信公眾號開發(fā)——1.接入微信公眾號,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2017-04-04
  • Node.js之網(wǎng)絡(luò)通訊模塊實(shí)現(xiàn)淺析

    Node.js之網(wǎng)絡(luò)通訊模塊實(shí)現(xiàn)淺析

    本篇文章主要介紹了Node.js之網(wǎng)絡(luò)通訊模塊實(shí)現(xiàn)淺析,具有一定的參考價(jià)值,有興趣的可以了解一下。
    2017-04-04
  • 基于Alpine Linux構(gòu)建前端node-web鏡像步驟詳解

    基于Alpine Linux構(gòu)建前端node-web鏡像步驟詳解

    這篇文章主要為大家介紹了基于Alpine Linux構(gòu)建前端node-web鏡像步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11

最新評論