美文网首页JAVA程序员今日看点
nodejs 爬取新浪微博用户信息

nodejs 爬取新浪微博用户信息

作者: Zoemings | 来源:发表于2017-03-23 18:21 被阅读562次

    1. 摘要

    微博作为新生网络应用形式, 在最近几年中得到了迅猛的发展,由于技术门槛低,微博的同质化问题非常严重,因此,发掘信息的主动发布者,是非常必要和有意义
    的。
    大致思路:根据一个人的关注递归查找出其他被关注者的关注,将信息存入数据库后,根据数据库的用户爬取对应的微博以及微博对应的评论,分析评论内容以及评论数量评估相应用户的影响指数。

    2. 实现工具

    • superagent
    • request
    • cherrio
    • mongodb

    3.数据库定义

    • 用户
    var userSchema = new Schema({
        uid:{type:String,unique:true},  //id唯一
        sex:String,                     //性别
        name: String  ,                 //昵称
        followCnt:  Number,             //所在地
        fansCnt:Number,                 //关注数
        weiboCnt:Number,                //微博数
        score:Number                    //评分
    });
    
    • 微博
    var newsSchema = new Schema({
        nId:{type:String,unique:true},
        content: String,    //内容
        author: {           //作者
          type: Schema.Types.ObjectId,
          ref: 'User'
        },
        isforward: Boolean,  //是否转发
        expandInfo:String    //转发内容
    });
    
    • 评论
    var commentSchema = new Schema({
        //当前评论微博
        news: {type: ObjectId, ref: 'News'},
        content: String,       //评论内容
        name:String              //评论人
    });
    

    4. 页面分析

    1.分析页面结构

    关注分析.png

    2.页面元素分析

    代码分析.png

    3.用cheerio 处理获取到的html

    首先将所有的每一个关注者<li>标签内容存储到数组中,data存储用户id,姓名,性别等基本信息,detail存储详细信息,包括关注数,粉丝数,微博数,identify存储会员,v标识等信息

    var follows = $('.follow_item ').toArray();
         for (var i = 0; i < follows.length; i++) {
              var data = $(follows[i]).attr("action-data").split("&");
              var detail = $(follows[i]).find(".info_connect span .count");
              var identify=$(follows[i]).find(".info_name a");
    

    前提条件:

    1. 基本信息为三即此人有姓名,id,性别;
    2. 详细信息为三即此人有粉丝,关注人,微博数
    3. 标识信息大于三即此人至少为微博会员或者大V
      目的:为了排除普通用户和特殊用户如下图用户


      排除.PNG
     if (data.length === 3 && detail.length === 3 && identify.length>=3 ) {
    

    分析出结果并存储,ps:用户id存储为flag前6位+uid,原因:实践表明,访问"http://weibo.com/" + uid+ "/follow"的时间比http://weibo.com/p/" + flag+uid+ "/follow慢得多

    var flag=$(follows[i]).find('.info_name a').attr('href').split(/[_=]/)[2].substr(0,6);
    var user={};
      user.uid = flag+data[0].split("=")[1];
      user.name = data[1].split("=")[1];
      user.sex = data[2].split("=")[1];
      user.followCnt = parseInt($(detail[0]).text());
      user.fansCnt = parseInt($(detail[1]).text());
      user.weiboCnt = parseInt($(detail[2]).text());
      saveUser(user);
    

    5. 模块代码

    • 模拟登陆模块
    function start() {
        var userName = "XXXX";  //用户
        var password = "XXXXX"; //密码
        var preLoginUrl = "http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=&rsakt=mod&checkpin=1&client=ssologin.js(v1.4.11)&_=" + (new Date()).getTime();
       async.waterfall([
            function (callback) {
                request({
                    "uri": preLoginUrl,
                    "encoding": "utf-8"
                }, callback);
            },
            function (res, body, callback) {
                var responseJson = getJsonObj(body);
                var loginUrl = 'http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.18)';
                var loginPostData = {
                    entry: "weibo",
                    gateway: "1",
                    from: "",
                    savestate: "7",
                    useticket: "1",
                    vsnf: "1",
                    su: "",
                    service: "miniblog",
                    servertime: "",
                    nonce: "",
                    pwencode: "rsa2",
                    rsakv: "xxxx",
                    sp: "",
                    sr: "xxxx",
                    encoding: "UTF-8",
                    prelt: "282",
                    url: "http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack",
                    returntype: "META"
                };
                //用户名base64加密
                loginPostData.su = new Buffer(userName).toString('base64');
                //密码RSA加密
                var rsaKey = new RsaEncrypt();
                rsaKey.setPublic(responseJson.pubkey, '10001');
                var pwd = rsaKey.encrypt([responseJson.servertime, responseJson.nonce].join("\t") + "\n" + password);
                loginPostData.sp = pwd;
                loginPostData.servertime = responseJson.servertime;
                loginPostData.nonce = responseJson.nonce;
                loginPostData.rsakv = responseJson.rsakv;
                //表单post
                request.post({
                    "uri": loginUrl,
                    "encoding": null,  
                    form: loginPostData
                }, callback);
            },
    
            function (res, body, callback) {
                body = iconv.decode(body,"GBK");
                var errReason = /reason=(.*?)\"/;
                var errorLogin = body.match(errReason);
                if (errorLogin) {
                    callback("登录失败,原因:" + errorLogin[1]);
                }
                else {
                    var urlReg = /location\.replace\(\'(.*?)\'\)./;
                    var urlLoginAgain = body.match(urlReg);           
                    if (urlLoginAgain) {
                        request({
                            "uri": urlLoginAgain[1],
                            "encoding": "utf-8"
                        }, callback);
                    }
                    else {
                        callback("match failed");
                    }
                }
            },
            function (res, body, callback) {
                console.log("开始分析... ");
                 getfollow("xxxx"); //源用户
            }
        ], function (err) {
            console.log(err)
        });
    }
    
    

    用户信息保存,保存成功后递归查询该用户的关注者信息

    function saveUser(user){
        var user = new User({
            uid:user.uid,
            sex:user.sex,
            name:user.name,
            followCnt:user.followCnt,
            fansCnt:user.fansCnt,
            weiboCnt:user.weiboCnt
        });
        user.save(function (err, usr) {
            if (err) {
                console.log("失败");
            }
            else {
                console.log(user);
                getfollow(user.uid);
                sleep(1000);
                console.log("保存成功");
            }
        });
    }
    

    参考:cheerio API

    6. 遇到的困难及措施

    递归并发访问会造成短时间大量请求,IP很容易被封,如下:

    哎.PNG

    1种是如上输入验证码可以继续,1种是你们太快了,之后就有一段时间完全无法访问微博,试想想,递归查找由一个用户源开始,爬取所有关注者的所有关注人,因此并发是难以想象的,这时setTimeout没有用,自动给你优化掉了。简单的sleep函数能有效阻塞。

    function sleep(milliSeconds) {
        var startTime = new Date().getTime();
        while (new Date().getTime() < startTime + milliSeconds);
    };
    

    参考:nodejs入门

    7. 爬取热门微博

    根据爬取存储进数据库的大V信息选取微博粉丝数目>500万,微博数大于100的大V的热门微博

    function reptileMove(url,callback){
        //延迟毫秒数
        var delay = parseInt((Math.random() * 10000000000000) % 7000, 10);
        setTimeout(function() {
            var ids=url.split(" ");
            var urls="http://weibo.com/p/"+ids[0]+"?profile_ftype=1&is_hot=1#_0";
            superagent
                .get(urls)
                .set('Accept', 'application/json')
                .set('User-Agent', 'BaiduSpider')
                .end(function (err, res) {
                    // sleep(1500);
                    count=count+1;
                    console.log(count+" "+urls);
                    var body =  res.text;
                    body.replace(/(\\n|\\t|\\r)/g, " ").replace(/\\/g, "");
                    var $ = cheerio.load(body);
                    var x=$('.WB_feed_detail .WB_detail ').toArray();
                    var y=$('.WB_feed_handle ul').toArray();
                    setTimeout(function () {
                    for(var i = 0; i < x.length; i++) {
                        var info=($(y[i]).find('li')).toArray();
                        var news={};
                        news.content=($(x[i]).find('.WB_text').text()).replace(/^(\s|\xA0)+|(\s|\xA0)+$/g, '');
                        news.time=($(x[i]).find('.WB_from  a').attr('title'));
                        news.nid=($(x[i]).find('.WB_from  a').attr('href')).split("?")[0];
                        news.commentCnt=($(info[2]).find('span em')).text().substring(1);       //评论
                        news.praiseCnt=($(info[3]).find('span em')).text().substring(1);        //点赞
                        news.author=ids[1];
                        saveNews(news);
                    }
                    }, delay)
    
                });
            callback(null,url +'Call back content');
        }, delay);
    }
    

    8. 代码地址

    https://github.com/HZNU-QUANTA/NODE-ZMM/tree/master/sina

    相关文章

      网友评论

        本文标题:nodejs 爬取新浪微博用户信息

        本文链接:https://www.haomeiwen.com/subject/ywjtottx.html