zoukankan      html  css  js  c++  java
  • 【nodejs】理想论坛帖子下载爬虫1.06

    //======================================================
    // 理想论坛帖子下载爬虫1.06
    // 循环改成了递归,但最多下载千余文件就崩了
    // 2018年4月27日
    //======================================================
    var http=require("http");            // http模块
    var zlib = require('zlib');            // 用于解析gzip网页
    var fs=require('fs');                // 文件处理模块
    var iconv = require('iconv-lite');    // 用于转码。
    var cheerio = require("cheerio");    // 用于从HTML中以类似jquery方式查找目标
    var async=require('async');            // 用于异步流程控制
    var EventProxy = require('eventproxy');// 用来控制并发
    
    //--- 下面为全局变量 ---
    var folder;// 存文件的目录
    var topics=[]; // 帖子数组
    var finalTopics=[];// 所有帖子加子贴的最终数组
    
    //-------------------------------
    // 用于创建目录
    //-------------------------------
    function createFolder(){
        console.log('准备创建目录');
    
        folder='infos('+currDateTime()+")";
        fs.mkdir('./'+folder,function(err){
            if(err){
                console.log("目录"+folder+"已经存在");
            }else{
                console.log("目录"+folder+"已创建");
            }
        });
    }
    
    //-------------------------------
    // 浏览页面找主贴
    // start:开始页,end:结束页
    //-------------------------------
    function findTopics(start,end){
        console.log('准备从以下页面寻找主贴');
    
        for(var i=start;i<=end;i++){        
            pageUrl='http://www.55188.com/forum-8-'+i+'.html'
            findTopicsInPage(pageUrl);
        }
    }
    
    //-------------------------------
    // 找到每个论坛页的帖子
    // pageUrl:论坛页的地址
    //-------------------------------
    function findTopicsInPage(pageUrl){
        console.log("page="+pageUrl);
    
        var currUrl=pageUrl.replace("http://","");
        var pos=currUrl.indexOf("/");
        var hostname=currUrl.slice(0,pos);    
        var path=currUrl.slice(pos);    
        pos=currUrl.lastIndexOf("/");
        var dir="http://"+currUrl.slice(0,pos);            
        
        var options={
            hostname:hostname,
                port:80,
                path:path,
              method:'GET',        
        };    
    
        var req=http.request(options,function(resp){
            var html = [];
    
            resp.on("data", function(data) {
                html.push(data);
            })
            resp.on("end", function() {
                var buffer = Buffer.concat(html);
    
                var body = iconv.decode(buffer,'gb2312');
                var $ = cheerio.load(body);            
    
                $("tbody").each(function(index,element){
                    var $tbody=cheerio.load($(element).html());
    
                    var topic={};
                    topic.pageCount=1;
                    topic.url=null;
                    topic.title=null;
    
                    $tbody(".forumdisplay a").each(function(index,element){                
                        var topicUrl='http://www.55188.com/'+$tbody(element).attr("href");
                        var topicTitle=$tbody(element).text();
    
                        topic.url=topicUrl
                        topic.title=topicTitle;                        
                    })
    
                    $tbody(".threadpages").each(function(index,element){
                        topic.pageCount=$tbody(element).children().last().text();
                    })
                    
                    if(topic.url!=null && topic.title!=null){
                        topics.push(topic);    
                    }
                })  
            }).on("error", function(err) {
                console.log("findTopicsInPage函数请求后获取响应时出现异常"+err);
            })
        });
    
        // 超时处理
        req.setTimeout(7500,function(){
            req.abort();
        });
    
        // 出错处理
        req.on('error',function(err){
            console.log('findTopicsInPage函数请求时发生错误'+err);  
        });
    
        // 请求结束
        req.end();    
    }
    
    //-------------------------------
    // 保存每个帖子到文件,完成一个递归调自己一次
    // Nodejs特殊的回调处理强迫我们把for,while循环改成递归方式
    //-------------------------------
    function saveTopicDetails(){
        if(finalTopics.length>0){
            var topic=finalTopics.pop();
    
            var topicUrl=topic.url;
            var topicTitle=topic.title;
            var index=topic.index;
    
            var currUrl=topicUrl.replace("http://","");
            var pos=currUrl.indexOf("/");
            var hostname=currUrl.slice(0,pos);    
            var path=currUrl.slice(pos);    
            pos=currUrl.lastIndexOf("/");
            var dir="http://"+currUrl.slice(0,pos);
            
            var options={
                hostname:hostname,
                    port:80,
                    path:path,
                  method:'GET',        
                     headers:{
                        'Connection':'close',     
                      }
            };    
    
            var req=http.request(options,function(resp){
                req.setSocketKeepAlive(false);
                var html = [];
    
                resp.on("data", function(data) {
                    html.push(data);
                })
                resp.on("end", function() {
    
                    try {
                        var buffer = Buffer.concat(html);
    
                        var body = iconv.decode(buffer,'gb2312');
                        var $ = cheerio.load(body);
                        var infos=[];// 获得的发帖人信息
    
                        // 得到发帖人信息
                        $(".postinfo").each(function(index,element){                    
                            var content=$(element).text();
                            content=content.replace(/s+/g,' ');// 空白字符替换为一个空格
                            var arr=content.split(" ");// 以空格劈分
                            
                            if(arr.length==7){
                                info={'url':topicUrl,
                                      'title':topicTitle,
                                      '楼层':arr[1],
                                      '作者':arr[2].replace('只看:',''),
                                      '日期':arr[4],
                                      '时间':arr[5]};
                                infos.push(info);
                                //console.log('info='+info);
                            }else if(arr.length==8){
                                info={'url':topicUrl,
                                      'title':topicTitle,
                                      '楼层':arr[1],
                                      '作者':arr[2].replace('只看:',''),
                                      '日期':arr[5],
                                      '时间':arr[6]};
                                infos.push(info);
                                //console.log('info='+info);
                            }
                        })
    
                        if(infos.length>0){
                            var text=JSON.stringify(infos);
    
                            filename='./'+folder+'/'+index+'.dat';
    
                            fs.writeFile(filename,text,function(err){
                                if(err){
                                    console.log('写入文件'+filename+'失败,因为'+err);
                                }
                            });
    
                            if(index % 50==0){
                                console.log(coloredText(nowTime()+'第'+index+'个文件保存完毕','green'));//让控制台出点动静
                            }
    
                            //req.end();
                            saveTopicDetails();//一个帖子完成,递归一次
                        }
                    } catch(e) { 
                        var text=nowTime()+"saveTopicDetail函数请求时(on_end)获取响应时出现异常"+e+'
    ';
                        text+=nowTime()+" url="+topicUrl+'
    ';
                        text+=nowTime()+" title="+topicTitle+'
    ';
                        text+=nowTime()+" index="+index+'
    ';
                        console.log(coloredText(text,'magenta'));
                        ///
                        //req.end();
                        saveTopicDetails();//出错后依旧递归
                    }
                }).on("error", function(err) {
                    var text=nowTime()+"saveTopicDetail函数请求后(on_error)获取响应时出现异常"+err+'
    ';
                    text+=nowTime()+" url="+topicUrl+'
    ';
                    text+=nowTime()+" title="+topicTitle+'
    ';
                    text+=nowTime()+" index="+index+'
    ';
                    console.log(coloredText(text,'red'));
                    ///
                    //req.end();
                    saveTopicDetails();//出错后依旧递归
                })
            });
    
            // 超时处理
            req.setTimeout(1000,function(){
                req.abort();
            });
    
            // 出错处理
            // 这里最容易出错 Error: socket hang up
            req.on('error',function(err){
                var text=nowTime()+"saveTopicDetails函数请求时出现异常"+err+'
    ';
                text+=nowTime()+" url="+topicUrl+'
    ';
                text+=nowTime()+" title="+topicTitle+'
    ';
                text+=nowTime()+" index="+index+'
    ';
                console.log(coloredText(text,'red'));
                ///
                req.end();
                //finalTopics.push(topic);
                saveTopicDetails();//出错后依旧递归
            });
    
            // 请求结束
            req.end();
        }
    }
    
    //-------------------------------
    // 入口函数
    // start:起始页,从1开始
    // end:终止页,>start
    //-------------------------------
    function main(start,end){
    
        var flow=require('nimble');
    
        flow.series([
            function(callback){
                setTimeout(function(){ 
                    createFolder();
                    callback();
                },100);
            },
    
            function(callback){
                setTimeout(function(){ 
                    findTopics(start,end);
                    callback();
                },100);
            },
    
            function(callback){
                setTimeout(function(){ 
                    var n=topics.length;
                    console.log("共找到"+n+"个帖子");
    
                    // 获得每个子贴所在地址,序号和标题
                    var index=0;
                    var arr=[];
                    for(var i=0;i<n;i++){
                        var topic=topics[i];
    
                        for(var j=1;j<=topic.pageCount;j++){
                            var regexp=new RegExp(/-(d+)-(d+)-(d+)/);
                            var topicUrl=topic.url.replace(regexp,"-$1-"+j+"-$3");// 用正则表达式替换第二个数字
                            
                            index++;
                    
                            var item={'index':index,'url':topicUrl,'title':topic.title};
                            arr.push(item);
                        }
                    }                
                    
                    finalTopics=arr;// 所有帖子加子贴的最终数组
                    finalTopics.reverse();
                    console.log('拟生成文件'+finalTopics.length+'个');
    
                    saveTopicDetails();
    
                    callback();
                },3000);
            },
        ]);
    }
    
    //--------------------------------------
    // 通用函数,返回当前日期时间 创建目录用
    //--------------------------------------
    function currDateTime() {
        var date = new Date();
        var seperator1 = "-";
        var seperator2 = "_";
        var month = date.getMonth() + 1;
        var strDate = date.getDate();
        if (month >= 1 && month <= 9) {
            month = "0" + month;
        }
        if (strDate >= 0 && strDate <= 9) {
            strDate = "0" + strDate;
        }
        var currentdate =date.getFullYear() + seperator1 + month + seperator1 + strDate
                + " " + date.getHours() + seperator2 + date.getMinutes()
                + seperator2 + date.getSeconds();
        return currentdate;
    }
    
    //--------------------------------------
    // 通用函数,返回当前日期时间 控制台输出时间用
    //--------------------------------------
    function nowTime() {
        var date = new Date();
        var seperator1 = "-";
        var seperator2 = ":";
        var month = date.getMonth() + 1;
        var strDate = date.getDate();
        if (month >= 1 && month <= 9) {
            month = "0" + month;
        }
        if (strDate >= 0 && strDate <= 9) {
            strDate = "0" + strDate;
        }
        var currentdate =date.getFullYear() + seperator1 + month + seperator1 + strDate
                + " " + date.getHours() + seperator2 + date.getMinutes()
                + seperator2 + date.getSeconds()+ " ";
        return currentdate;
    }
    
    //-------------------------------
    // 得到带颜色(前景色)的文字,用于在控制台输出
    // text:文字,color:前景色
    //-------------------------------
    function coloredText(text,color){
        var dic = new Array();
        dic["white"] = ['x1B[37m', 'x1B[39m'];
        dic["grey"] = ['x1B[90m', 'x1B[39m'];
        dic["black"] = ['x1B[30m', 'x1B[39m'];
        dic["blue"] = ['x1B[34m', 'x1B[39m'];
        dic["cyan"] = ['x1B[36m', 'x1B[39m'];
        dic["green"] = ['x1B[32m', 'x1B[39m'];
        dic["magenta"] = ['x1B[35m', 'x1B[39m'];
        dic["red"] = ['x1B[31m', 'x1B[39m'];
        dic["yellow"] = ['x1B[33m', 'x1B[39m'];
    
        return dic[color][0]+text+dic[color][1];
    }
    
    // 开始
    main(2,2);
  • 相关阅读:
    8.电影推荐
    一.Memcached企业服务
    7.学完linux系统运维到底可以做什么?
    svn+jenkins自动部署
    关于gitlab+jenkins自动部署代码的实现
    Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggre
    php实现雪花算法(ID递增)
    php使用rdkafka进行消费
    Burp破解安装(1.7和2.0)
    在已有lnmp环境的基础上安装PHP7
  • 原文地址:https://www.cnblogs.com/heyang78/p/8964710.html
Copyright © 2011-2022 走看看