zoukankan      html  css  js  c++  java
  • 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();
    }
  • 相关阅读:
    奇怪的人
    假象世界
    心态记录
    民用自组织网络公司概要
    禁止VMware虚拟机与Host的时间同步
    20万左右SUV介绍
    手机GPS为什么能在室内定位?
    取余与位运算
    shell 基础进阶 *金字塔
    shell 、awk两种方法编写9*9法表
  • 原文地址:https://www.cnblogs.com/LandMine/p/5443166.html
Copyright © 2011-2022 走看看