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.分析页面结构
关注分析.png2.页面元素分析
代码分析.png3.用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");
前提条件:
- 基本信息为三即此人有姓名,id,性别;
- 详细信息为三即此人有粉丝,关注人,微博数
-
标识信息大于三即此人至少为微博会员或者大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("保存成功");
}
});
}
6. 遇到的困难及措施
递归并发访问会造成短时间大量请求,IP很容易被封,如下:
哎.PNG1种是如上输入验证码可以继续,1种是你们太快了,之后就有一段时间完全无法访问微博,试想想,递归查找由一个用户源开始,爬取所有关注者的所有关注人,因此并发是难以想象的,这时setTimeout没有用,自动给你优化掉了。简单的sleep函数能有效阻塞。
function sleep(milliSeconds) {
var startTime = new Date().getTime();
while (new Date().getTime() < startTime + milliSeconds);
};
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);
}
网友评论