亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

利用node.js爬取指定排名網站的JS引用庫詳解

2019-11-19 15:59:42
字體:
來源:轉載
供稿:網友

前言

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

所用到的node主要模塊

  • express 不用多說
  • request http模塊
  • cheerio 運行在服務器端的jQuery
  • node-inspector node調試模塊
  • node-dev 修改文件后自動重啟app

關于調試Node

在任意一個文件夾,執行node-inspector,通過打開特定頁面,在頁面上進行調試,然后運行app,使用node-dev app.js來自動重啟應用。

所碰到的問題

1. request請求多個頁面

由于請求是異步執行的,和分別返回3個頁面的數據,這里只爬取了50個網站,一個頁面有20個,所以有3頁,通過循環里套request請求,來實現。

通過添加請求頭可以實現基本的反爬蟲

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

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. 多層回調

仔細觀察代碼,你會發現,處理數據的方法使用了如下的多層回調,也可以不使用回調,寫在一個函數內部;因為,每層都要使用上一層的數據,造成了這樣的寫法。

function f1(data1){ f2(data1);}function f2(data2){ f3(data2);}function f3(data3){ f4(data4);}

3. 正則獲取JS庫

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

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

獲取到的script可能是上面這樣的,由于庫名的命名真是各種各樣,后來想了一下,因為文件名是用.js結尾的,所以就以點號為結尾,然后把點號之前的字符截取下來,這樣獲得了庫名,代碼如下。

var reg = /[^////]+$/g;var libName = jsLink.match(reg).join('');var libFilter = libName.slice(0,libName.indexOf('.'));

4.cheerio模塊獲取JS引用鏈接

這部分也花了一點時間,才搞定,cheerio獲取DOM的方法和jQuery是一樣的,需要對返回的DOM對象進行查看,就可以看到對象里隱藏好深的href屬性,方法大同小異,你也可以使用其他選擇器,選擇到script標簽

var $ = cheerio.load(body);var scriptFile = $('script').toArray();scriptFile.forEach(function(item,index){ if (item.attribs.src != null) { obtainLibName(item.attribs.src,index);}

5.存儲數據到數據庫

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

// 將緩存數據存儲到數據庫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('保存數據出錯' + err); }); } }) })(i) } console.log('一共存儲' + libObj.length + '條數據到數據庫');}

6.分頁插件

本爬蟲前端使用了bootstrap.paginator插件,主要是前臺分頁,返回數據,根據點擊的頁數,來顯示對應的數據,后期考慮使用AJAX請求的方式來實現翻頁的效果,這里的注意項,主要是最后一頁的顯示,最好前面做個判斷,因為返回的數據,不一定剛好是頁數的整數倍

function _paging(libObj) { var ele = $('#page'); var pages = Math.ceil(libObj.length/20); console.log('總頁數' + 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('當前選中第:' + 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 + '條數據'); 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);// 點擊顯示按鈕,顯示前20項數據 _paging(data); } ) }); } //翻頁器 function _paging(libObj) { var ele = $('#page'); var pages = Math.ceil(libObj.length/20); console.log('總頁數' + 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('當前選中第:' + 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('查詢出現錯誤' + 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鏈接存為數組 console.log('網站爬取中``') for (var i = 0; i < 10; i++) { // ***這里后面要改,默認爬取前10名 var url = sitesArr[i].attribs.href; sites.push(url);//保存網址,添加wwww前綴 } console.log(sites); console.log('一共爬取' + sites.length +'個網站'); console.log('存儲數據中...') 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('出現錯誤: '+err); var $ = cheerio.load(body); var scriptFile = $('script').toArray(); callback(scriptFile,options.url); }) })(j,storeLib) }; function storeLib(scriptFile,url){ flag++;// 是否存儲數據的標志 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和數據庫里存儲數據,防止重復儲存 // calcNum(src);//存儲數據到數據庫 // ***這里后面要改,默認爬取前10名 var libSrc = calcNum(src); store2db(libSrc); } })(flag,urls.length,src) } }// getScript END// 將緩存數據存儲到數據庫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('保存數據出錯' + err); }); } }) })(i) } console.log('一共存儲' + libObj.length + '條數據到數據庫');}// 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下載地址 (本地下載

后記

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

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲欧美另类中文字幕| 中文字幕久热精品在线视频| 色爱av美腿丝袜综合粉嫩av| 亚洲一区亚洲二区| 欧美国产第二页| 国产精品久久久久aaaa九色| 亚洲精品网址在线观看| 国产精品视频最多的网站| 国产精品视频播放| 91色琪琪电影亚洲精品久久| 91精品视频免费| 国产成人精品在线观看| 国产日韩欧美在线看| 亚洲精品成人免费| 久久久久这里只有精品| 亚洲人成欧美中文字幕| 国产精品麻豆va在线播放| 成人在线免费观看视视频| 欧美日韩午夜视频在线观看| 日韩在线观看高清| 亚州精品天堂中文字幕| 亚洲欧美另类中文字幕| 亚洲国产天堂久久综合网| 国产99久久精品一区二区| **欧美日韩vr在线| 欧美日韩免费在线观看| 久久久91精品国产一区不卡| 国产成人一区三区| 国产精品免费视频xxxx| 日韩在线精品视频| 中文字幕在线观看日韩| 日韩大片在线观看视频| 日韩欧美视频一区二区三区| 亚洲新声在线观看| 欧美日韩成人免费| 亚洲视频axxx| 亚洲**2019国产| 国产精品美女在线| 欧美日韩成人网| 国产一区二区三区视频| 国产国语videosex另类| 国产一区二区三区在线免费观看| 国产欧美一区二区白浆黑人| 中文字幕久精品免费视频| 亚洲无亚洲人成网站77777| 在线播放国产一区二区三区| 久久99久久亚洲国产| 国产一区二区视频在线观看| 成人动漫网站在线观看| 欧美日韩加勒比精品一区| 欧美在线视频免费| 色综合导航网站| 欧美高清无遮挡| 国产性猛交xxxx免费看久久| 国产情人节一区| 久久久亚洲欧洲日产国码aⅴ| 欧美一级淫片丝袜脚交| 亚洲第一男人av| 亚洲欧美综合图区| 国产女同一区二区| 欧美在线视频观看免费网站| 亚洲精品电影网在线观看| 久99九色视频在线观看| 蜜臀久久99精品久久久无需会员| 日韩av电影在线播放| 亚洲欧美日韩国产中文专区| 成人免费视频网址| 国产精品久久久久aaaa九色| 91chinesevideo永久地址| 69国产精品成人在线播放| 国产97在线观看| 久久精品这里热有精品| 亚洲精品久久久久中文字幕欢迎你| 国产亚洲精品激情久久| 激情av一区二区| 欧美激情啊啊啊| 久久精品在线播放| 68精品久久久久久欧美| 亚洲国产精品成人精品| 欧美中文字幕视频在线观看| 国产精品久久国产精品99gif| 亚洲精品免费网站| 欧美精品第一页在线播放| 亚洲国产三级网| 国产精品激情av电影在线观看| 欧美美女15p| 国产精品扒开腿做爽爽爽的视频| 国外日韩电影在线观看| 国产精品成人av性教育| xvideos国产精品| 成人免费网视频| 久久久免费观看| 欧美老女人bb| 欧美性xxxx在线播放| 欧美一级免费视频| 热久久免费视频精品| 亚洲精品电影在线观看| 日韩av在线网站| 懂色aⅴ精品一区二区三区蜜月| 国产精品影片在线观看| 国产免费一区视频观看免费| 国内精品中文字幕| 日韩小视频网址| 国产精品一区二区久久精品| 中文精品99久久国产香蕉| 91九色综合久久| 91九色在线视频| 日韩电影在线观看中文字幕| 国产成人91久久精品| 国产精品香蕉av| 18性欧美xxxⅹ性满足| 中文精品99久久国产香蕉| 国产美女扒开尿口久久久| 久久精品视频在线观看| 亚洲国产精品国自产拍av秋霞| 精品久久久久久久久久| 色yeye香蕉凹凸一区二区av| 久久久人成影片一区二区三区观看| 久久精品国产精品亚洲| 中文字幕不卡av| 久久久久久久久久久成人| 精品久久久一区二区| 成人激情视频在线观看| 日韩av手机在线| 国内外成人免费激情在线视频| 一本一道久久a久久精品逆3p| 91久久国产综合久久91精品网站| 久久伊人色综合| 亚洲国产又黄又爽女人高潮的| 久久久久久久91| 日韩av片永久免费网站| 精品国产网站地址| 国产欧美日韩高清| 性欧美在线看片a免费观看| 亚洲国产精品小视频| 欧美亚洲视频在线观看| 精品成人av一区| 日韩中文字幕免费看| 北条麻妃99精品青青久久| 久久精品国产一区二区三区| 成人羞羞国产免费| 国产精品69久久久久| 日韩性生活视频| www.午夜精品| www日韩中文字幕在线看| 久久视频在线播放| 精品中文字幕在线2019| 日韩精品免费在线视频观看| 久久久国产影院| xxx欧美精品| 韩国精品美女www爽爽爽视频| 日韩在线免费观看视频| 精品久久香蕉国产线看观看gif| 日韩中文字幕网| 日韩电影中文字幕在线观看| 亚洲性视频网站| 国外视频精品毛片| 亚洲美女又黄又爽在线观看| 亚洲人成网站777色婷婷| 欧美成年人视频网站| 欧美日韩一区二区免费在线观看| 久久男人资源视频| 亚洲嫩模很污视频| 欧美激情国产日韩精品一区18|