登录部分的完整源代码见WeiboRobot
其实多数尝试动手写新浪微博爬虫的童鞋都知道,新浪微博虽然为用户提供了API调用的接口。但是依然是存在调用频率限制、API设计不完整等诸多问题。因此在某些需要大规模或者详细数据获取的一些研究我们还是得自己动手解析新浪微博的网页。
要基于解析页面的方式进行微博爬虫的撰写其实主要解决两个问题,首先一个问题就是新浪微博的数据访问必须是要登录的。那么下面就关于新浪微博登录的模块将自己的实现和大家分享。
新浪微博的登录完整阶段分为三个部分(这里的步骤以及各个步骤中的实现可能会随着微博版本的不同而有所差异在这里推荐使用Fiddler软件观察微博的登录过程然后进行模拟):
1、预登陆:这个阶段需要对用户名进行Base64算法加密然后进行预登陆主要原因是获取服务器端参数servertime、nonce 、rsakv、pubkey 用于后续的登录以及用户密码的加密,具体代码如下:
public boolean preLogin() {
boolean flag = false;
try {
su = Base64.encodeBase64String(URLEncoder.encode(username, "UTF-8")
.getBytes());
String url = "http://login.sina.com.cn/sso/prelogin.php?"
+ "entry=weibo"
+ "&callback=sinaSSOController.preloginCallBack" + "&su="
+ su + "&rsakt=mod&client=ssologin.js(v1.4.11)&_="
+ getTimestamp();
String content;
content = getContentGBK(getRequest(client, url.trim()));
int startIndex = content.indexOf("(");
content = content.substring(startIndex + 1, content.length() - 1);
JSONObject json = JSONObject.parseObject(content);// 获取相关的参数用于加密登录
servertime = json.getLongValue("servertime");
nonce = json.getString("nonce");
rsakv = json.getString("rsakv");
pubkey = json.getString("pubkey");
flag = encodePwd();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return flag;
}
2、登录:这个阶段我们需要使用步骤1中获取的参数进行用户密码的加密以及填充请求参数。用户名的加密算法同1,至于用户密码的加密目前新浪微博是采用RSA算法加密,这个我没有详细研究就是直接调用新浪的js进行加密。在执行这个请求之后服务器端会返回一个retcode和重定向的URL,如果 retcode=0 说明正常,继续访问重定向url,这时候我们才能够获取相应的Cookies,也即登录过程结束。
密码加密算法调用模块:
private boolean encodePwd() {
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine se = sem.getEngineByName("javascript");
try {
se.eval(new FileReader("./\\encoder\\encoder.js"));
Invocable invocableEngine = (Invocable) se;
String callbackvalue = (String) invocableEngine.invokeFunction(
"encodePwd", pubkey, servertime, nonce, password);
sp = callbackvalue;
return true;
} catch (FileNotFoundException e) {
try {
Log.addLog("加密脚本encoder.js未找到");
} catch (IOException e1) {
e1.printStackTrace();
}
} catch (ScriptException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
errInfo = "密码加密失败";
return false;
}
登录模块代码:
public boolean login() {
if (preLogin()) {
String url = "http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.11)";
// 设置参数
List<NameValuePair> parms = new ArrayList<NameValuePair>();
parms.add(new BasicNameValuePair("entry", "weibo"));
parms.add(new BasicNameValuePair("gateway", "1"));
parms.add(new BasicNameValuePair("from", ""));
parms.add(new BasicNameValuePair("savestate", "7"));
parms.add(new BasicNameValuePair("useticket", "1"));
parms.add(new BasicNameValuePair(
"pagerefer",
"http://login.sina.com.cn/sso/logout.php?entry=miniblog&r=http%3A%2F%2Fweibo.com%2Flogout.php%3Fbackurl%3D%252F"));
parms.add(new BasicNameValuePair("vsnf", "1"));
parms.add(new BasicNameValuePair("su", su));
parms.add(new BasicNameValuePair("service", "miniblog"));
parms.add(new BasicNameValuePair("servertime", servertime + ""));
parms.add(new BasicNameValuePair("nonce", nonce));
parms.add(new BasicNameValuePair("pwencode", "rsa2"));
parms.add(new BasicNameValuePair("rsakv", rsakv));
parms.add(new BasicNameValuePair("sp", sp));
parms.add(new BasicNameValuePair("encoding", "UTF-8"));
parms.add(new BasicNameValuePair("prelt", "187"));
parms.add(new BasicNameValuePair(
"url",
"http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack"));
parms.add(new BasicNameValuePair("returntype", "META"));
try {
/*
* 第一次访问
*/
CloseableHttpResponse response = postRequest(client, url.trim(), parms);
String html = getContentGBK(response);
//Log.addLog(html);
// 获取重定向的url
Pattern p = Pattern.compile("location.replace\\(.*?\\)");
Matcher m=null ;
if(html!=null)
m = p.matcher(html);
String redirect_uri = "";
if (m!=null&&m.find()) {
redirect_uri = m.group(0); // retcode=0 说明正常,继续访问重定向url
int start_index = redirect_uri.indexOf('\'');
redirect_uri = redirect_uri.substring(start_index + 1,
redirect_uri.length() - 1);
}
if (redirect_uri.indexOf("retcode=0") > -1) {
HttpGet get = new HttpGet(redirect_uri);
response = client.execute(get);
return true;
}
response.close();
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
}
}
return false;
}
最后补充一句,本文使用了httpclient-4.3辅助开发.