使用node爬虫,爬取指定排名网站的JS引用库

news/2024/4/27 14:37:05/文章来源:https://blog.csdn.net/weixin_33912453/article/details/89008656

前期准备

本爬虫将从网站爬取排名前几的网站,具体前几名可以具体设置,并分别爬取他们的主页,检查是否引用特定库。

github地址

所用到的node主要模块

  • express 不用多说

  • request http模块

  • cheerio 运行在服务器端的jQuery

  • node-inspector node调试模块

  • node-dev 修改文件后自动重启app

关于调试Node

在任意一个文件夹,执行node-inspector,通过打开特定页面,在页面上进行调试,然后运行app,使用node-dev app.js来自动重启应用。

所碰到的问题

1. request请求多个页面

  1. 由于请求是异步执行的,和分别返回3个页面的数据,这里只爬取了50个网站,一个页面有20个,所以有3页,通过循环里套request请求,来实现。

  2. 通过添加请求头可以实现基本的反爬虫

  3. 处理数据的方法都写在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);// 传递 HTMLvar 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;

后记

通过这个小爬虫,学习到很多知识,例如爬虫的反爬虫有哪些策越,意识到node.js的异步执行特性,前后端是怎么进行交互的。同时,也意识到有一些方面的不足,后面还需要继续改进,欢迎大家的相互交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_752469.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Nginx的网站架构

架构如上图1.LB &#xff08;Nginx负载均衡器&#xff09;Nginx Keepalived2.Web &#xff08;Nginx 服务器&#xff09;Nginx PHP Mysql ClientNRPE3.DB &#xff08;Mysql 主从&#xff09;Mysql 主 Mysql 从 shell 备份 NRPE4.Monitor &#xff08;Nagios监控&#xf…

利用上载漏洞,攻击asp.net 网站

为什么80%的码农都做不了架构师&#xff1f;>>> 偶然发现有些网站存在上载漏洞&#xff0c; 于是乎我利用闲暇时间写了一个小工具。 这个工具主要可以用来列出网站所有文件 &#xff0c; 上传下载、删除网站文件等功能 。 将以下代码保存为文件 lkfile.aspx &#…

如何使用Tableau分析敏捷,开发和网站指标

开发人员在设计&#xff0c;开发&#xff0c;测试&#xff0c;部署和管理应用程序时使用了许多生产力&#xff0c;编码&#xff0c;测试和云管理工具。 尽管大多数工具都提供报告和分析功能&#xff0c;但技术团队可能有充分的理由开发自己的报告功能&#xff0c;以更好地分析&…

大型网站架构之分布式消息队列

大型网站架构之分布式消息队列 以下是消息队列以下的大纲&#xff0c;本文主要介绍消息队列概述&#xff0c;消息队列应用场景和消息中间件示例&#xff08;电商&#xff0c;日志系统&#xff09;。 本次分享大纲 消息队列概述消息队列应用场景消息中间件示例JMS消息服务常用消…

大型网站架构设计(图)

>传统网站架构与优化&#xff08;图&#xff09;

分布式网站架构:缓存在分布式系统中的应用

缓存是分布式系统中的重要组件&#xff0c;主要解决高并发&#xff0c;大数据场景下&#xff0c;热点数据访问的性能问题。提供高性能的数据快速访问。 一、缓存概述 缓存是分布式系统中的重要组件&#xff0c;主要解决高并发&#xff0c;大数据场景下&#xff0c;热点数据访…

教师节快乐,全网最全编程学习网站汇总来了,还不赶快收藏

文章目录一、在线教程1、how2j.cn2、w3cschool3、菜鸟教程4、易百教程5、码农教程6、简单教程7、Break易站8、C语言中文网9、并发编程网10、jenkov.com11、baeldung.com二、视频教程1、B站2、慕课网3、中国大学MOOC4、网易云课堂5、实验楼6、我要自学网7、大学生自学网8、极客学…

5个最受欢迎的编程挑战网站

译  原文地址&#xff1a;https://dev.to/surajsrv11/5-most-popular-coding-challenge-website-4bh4 简介 熟能生巧 这句谚语对每个人有用。因此&#xff0c;我提供了5个最受欢迎的&#xff08;编程&#xff09;网站&#xff0c;在那里你可以通过不同的真实场景的问题来学习练…

JSP网站开发基础总结《二》

有了上一篇的学习&#xff0c;我相信大家对于JSP一定有了一定的认识&#xff0c;从今天开始我们真正开启JSP模式&#xff0c;如果你有HTML的基础&#xff0c;那学起JSP来也就方便了很多了&#xff0c;首先JSP做为网站开发语言&#xff0c;它与HTML有很多相似的地方。网站是什么…

【django】京东等大型网站的混合搜索是怎么实现的?

混合搜索在各大网站如京东、淘宝都有应用&#xff0c;他们的原理都是什么呢?本博文将为你介绍它们的实现过程。 混合搜索的原理&#xff0c;用一句话来说就是&#xff1a;关键字id进行拼接。 混合搜索示例&#xff1a; 数据库设计&#xff1a; 视频方向&#xff1a; class Dir…

【django】京东等大型网站的混合搜索是怎么实现的?

混合搜索在各大网站如京东、淘宝都有应用&#xff0c;他们的原理都是什么呢?本博文将为你介绍它们的实现过程。 混合搜索的原理&#xff0c;用一句话来说就是&#xff1a;关键字id进行拼接。 混合搜索示例&#xff1a; 数据库设计&#xff1a; 视频方向&#xff1a; class Dir…

【django】京东等大型网站的混合搜索是怎么实现的?

混合搜索在各大网站如京东、淘宝都有应用&#xff0c;他们的原理都是什么呢?本博文将为你介绍它们的实现过程。 混合搜索的原理&#xff0c;用一句话来说就是&#xff1a;关键字id进行拼接。 混合搜索示例&#xff1a; 数据库设计&#xff1a; 视频方向&#xff1a; class Dir…

c++builder 运行网站的api_.NET Core 微服务:Ocelot的API网关实现

(给DotNet加星标&#xff0c;提升.Net技能)转自&#xff1a;另一个老李cnblogs.com/SteveLee/p/Ocelot_Api_http_and_https.html微服务架构什么是网关&#xff1f;通过DotNetty构建的远程RPC框架《.NET跨平台RPC框架DotNettyRPC》&#xff0c;已经实现了远程客户端的调用&#…

国内图片网站Yupoo的架构

之前向大家介绍过全球最大在线图片服务网站Flickr网站架构&#xff0c;Yupoo&#xff08;又拍网&#xff09;作为国内最大的图片服务提供商&#xff0c;我们也一起来看看它的架构&#xff0c;同样是提供图片服务&#xff0c;看看他与Flickr的差别在哪里&#xff0c;大家看完本文…

LOL钓鱼网站实战渗透

点击上方蓝字关注我们相信很多人都有遇到过这样的经历&#xff0c;无意中点到一些钓鱼网站&#xff0c;然后就泄露了自身信息&#xff0c;造成了一定的损失&#xff0c;对于这样的网站各位需警惕&#xff0c;千万不要乱点击来历不明的网站。今天我就来说说钓鱼网站的事&#xf…

查看网站所有会话_PHP-会话控制

会话控制因为 HTTP 是无状态的协议&#xff0c;没有办法记录多个事务请求间的状态。即访问一个页面请求后再请求另一个页面时无法判断两次请求来自同一下用户。会话原理在PHP中实现会话的原理是为用户分配一个唯一的加密ID&#xff0c;并保存在用户客户端&#xff0c;并在整个会…

网站URL网址末尾是否应该使用反斜杠

2019独角兽企业重金招聘Python工程师标准>>> 当对网站进行SEO优化时&#xff0c;难免会遇到因为URL导致的重复页面问题&#xff0c;其中一个比较常见的现象就是因页面地址后是否有添加反斜杠造成的&#xff0c;举例如下&#xff1a; 链接A&#xff1a; www.example.…

Redis网站热搜关键词加载实践,建议收藏

侠梦的开发笔记回复【面试题】获取2021年最新java面试题合集&#xff5e;来源&#xff1a;Catcher8www.cnblogs.com/catcher1994/p/5877262.html对于一个网站来说&#xff0c;无论是商城网站还是门户网站&#xff0c;搜索框都是有一个比较重要的地位&#xff0c;它的存在可以说…

一例千万级pv高性能高并发网站架构

2019独角兽企业重金招聘Python工程师标准>>> 受CU管理员的邀请参考“千万级pv高性能高并发网站架构与设计交流探讨帖”主题的交流&#xff0c;发表了一案例与大家分享。 一个支撑千万级PV的网站是非常考验一个架构是否成熟、健壮(本文不涉及软件架构的层面&am…

黑科技Python轻松爬取网站信息,看完我是佩服得五体投地!

1. 引言 本文主要介绍如何使用Scrapy结合PhantomJS采集天猫商品内容&#xff0c;文中自定义了一个DOWNLOADER_MIDDLEWARES&#xff0c;用来采集需要加载js的动态网页内容。看了很多介绍DOWNLOADER_MIDDLEWARES资料&#xff0c;总结来说就是使用简单&#xff0c;但会阻塞框架&a…