nodejs实现简单的爬虫
闲着无聊就想鼓捣点东西玩玩,所以决定用node做个爬虫来爬点数据。查了些资料鼓捣了一段时间也算是弄出了个简单的爬虫。
目前这只小爬虫还是有不少缺陷,不过爬点简单的静态页面上的内容已经足够了。
详细的使用说明我就不介绍了,在github中有。地址:https://github.com/XLandMine/node-crawler
相关代码的说明我也都有注释,需要注意的是我对于爬取深度的理解是url的path有几个‘/’即为几层深度,默认为5。
主要的实现步奏就是利用http或者https模块发送request请求。然后解析获得的html文本中所有a链接的href的值。再对获取到的url值进行过滤,保留可以使用的url到待解析的队列中。不管请求是否成功都会再次调用爬取crawl方法去待解析队列中获取url地址再次解析。
var http = require("http"); var https = require("https"); var urlUtil = require("url"); //获得a标签的链接 var urlReg = /<a.*?href=['"]([^"']*)['"][^>]*>/gmi; //将要抓取的url队列 var newUrlQueen = []; //已经抓取完毕的url队列 var oldUrlQueen = []; //几率抓取结果 var crawlCount = { total:0, success:0, failure:0 }; var Crawler = function (opt){ //抓取深度 this.deepCount = ( opt.deepCount * 1 ) || 5; //抓取开始的第一个URL this.firstUrl = opt.firstUrl || ''; //URL地址过滤 false不加入抓取队列 this.filterUrl = typeof opt.filterUrl == "function" ? opt.filterUrl : function(url){return true;}; //对抓取的html字符串进行解析 this.paresHtml = typeof opt.paresHtml == 'function' ? opt.paresHtml : function(html){}; if (this.firstUrl) { newUrlQueen.push(this.firstUrl); } } //解析html文本中所有a链接的url Crawler.parseUrl = function(html){ var urlArr = []; var m = null; while( m = urlReg.exec(html) ){ //利用正则将解析到的url保存到数组 urlArr.push(m[1]); } return urlArr; } //url过滤规则 Crawler.filterUrl = function( url , c){ var uObj , deep; //判断队列中是否有该url if (oldUrlQueen.indexOf( url ) > -1 || newUrlQueen.indexOf( url ) > -1) { return false}; uObj = urlUtil.parse(url); //判断是否是锚链接 if (uObj.hash) { return false}; if (uObj.path) { //只保留path的 / 字符串 deep = uObj.path.replace(/[^//]/g,"").length; //判断是否超过设定的页面深度 if ( deep >= c.deepCount ) { return false }; } //调用对象自定义的filterUrl方法 return c.filterUrl(url); } //开始爬取队列中的url Crawler.prototype.crawl = function(){ if (newUrlQueen.length > 0) { //从队列中取出要爬取的url var url = newUrlQueen.shift(); //填入爬取过的url oldUrlQueen.push(url); //开始爬取 this.senReq(url) }else { console.log("抓取结束,此次抓取统计:"); console.log(crawlCount); } } //发送请求 Crawler.prototype.senReq = function(url){ var req = ''; var oOptions = urlUtil.parse(url); oOptions.headers = { "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36", }; if(url.indexOf("https") > -1){ req = https.request(oOptions); } else if (url.indexOf("http") > -1) { req = http.request(oOptions); } else { //该url不是http或者https协议,放弃掉 this.crawl(); return ; } var self = this; req.on("response",function(res){ var data = ''; res.setEncoding('utf8'); res.on('data', function(chunk){ data += chunk; }); res.on('end', function(){ //html文本下载完成,进行处理 self.handleSuccess(data,url); data = null; }) }); req.on("error",function(err){ self.handleFailure(err,url); }); req.on("finish",function(){ console.log("开始请求地址",url) crawlCount.total++; }); req.end(); } //请求成功 Crawler.prototype.handleSuccess = function(data,url){ console.log("请求成功url:",url); crawlCount.success++; //解析页面所有url var urlArr = Crawler.parseUrl(data); var self = this; urlArr.forEach(function(v){ //过滤不符合条件的url,符合条件的才会被加入待查询url队列 if ( Crawler.filterUrl( v , self ) ) { newUrlQueen.push(v) }; }) //解析页面所需要的数据 this.paresHtml(data); //继续抓取 this.crawl(); } //请求失败 Crawler.prototype.handleFailure = function(err,url){ console.log("请求失败url:",url); console.log("失败log:",err); // console.log(err); crawlCount.failure++; this.crawl(); }