===============2011/06/28更新==========================
更新了HttpClient 4.x 模拟登录新浪微博的示例代码,经测试能正常登录,HttpClient 3.x和Perl版本的代码没有更新,有需要的同学自己参考HttpClient 4.x改写一下应该就可以了。希望大家也多一点探索精神,我没有精力经常来更新这个代码。
===============2011/05/24更新==========================
添加了用HttpClient 4.x 模拟登录新浪微博的示例代码
===============2011/05/22更新==========================
今天测试了日志中的Java代码,发现至少在目前是好用的,但是不保证以后会一直好用。感谢viLuo反馈的这个链接 新浪微博如何实现 SSO(t.sina.com.cn/weibo.com) 的分析,里面的内容写得很好很详细,遇到问题的同学可以去学习研究一下。
===============2011/03/29更新==========================
在最开始写这篇日志的时候,文中描述的两种方法的代码都经过了测试,能够成功登录。但是新浪的登录参数可能不停地在变化,所以如果你把日志中的代码直接复制回去,不能运行是很正常的情况。这里是文初写的模拟登录的一篇博客,里面涉及到了更多细节,有兴趣的朋友可以去看看。感谢zhaojiguang在评论中反馈此链接。
====================================================
现在新浪微博已经开放了API,为开发提供了很大的方便。但是仍然有一些事情是API所无能为力的,这时候就需要用到其他办法了。
1. 获得登录参数
一般的网站登录窗口都是一个简单的表单,随便搞一段代码就可以很轻易地登录上去,但是新浪的登录窗口却是由javascript控制的,稍微麻烦一些。对于简单的表单,可以通过查看html源代码获得登录参数;但是对于新浪这种,需要更深入一点的分析才行。
那么,要怎么办呢?答案是:万能的抓包工具。下面的截图分别展示了用Wireshark和Live HTTP headers(Firefox的一款插件)来获取登录参数的情况。
(1) Wireshark
抓包后找到带有 POST /sso/login.php?client=ssologin.js(v1.x.xx) HTTP/1.1的一行
然后右键,选择Follow TCP Stream,就会出来想要的结果,如图。注意看空行下面的红色文字部分。
(2) Live HTTP headers
实际上这个不算抓包工具,但是用起来更简单。注意看图中蓝底白字部分。
2. Java版
当获得了这些登录所需要传给服务器的参数以后,接下来的工作就比较简单了。
Java版主要用到了HttpClient,代码原始来源:http://flysnail.net/?p=27 ,我稍作了修改。
新浪微博中有一些页面登录前和登录后看到的内容不一样,比如 http://t.sina.com.cn/pub/tags ,程序中访问这个页面来检验是否登录成功。
(1) HttpClient 3.x版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import java.io.IOException; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; public class SinaLogin { public static String login(String email, String passwd) throws HttpException, IOException { PostMethod post = new PostMethod("http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.3.11)"); post.addParameter("service", "miniblog"); post.addParameter("client", "ssologin.js(v1.3.11)"); post.addParameter("entry", "miniblog"); post.addParameter("encoding", "utf-8"); post.addParameter("gateway", "1"); post.addParameter("savestate", "7"); post.addParameter("from", ""); post.addParameter("useticket", "0"); post.addParameter("username", email); post.addParameter("password", passwd); post.addParameter("url", "http://t.sina.com.cn/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack"); post.addParameter("returntype", "META"); HttpClient client = new HttpClient(); client.executeMethod(post); GetMethod get = new GetMethod("http://t.sina.com.cn/pub/tags"); client.executeMethod(get); System.out.println(new String(get.getResponseBody())); return new String(get.getResponseBody()); } public static void main(String[] args) throws HttpException, IOException { login("email","password"); } } |
(2) HttpClient 4.x版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | package com.icycandy; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; public class SinaLogin { private final static HttpClient client = new DefaultHttpClient(); /** * 抓取网页 * * @param url * @throws IOException */ static String get(String url) throws IOException { HttpGet get = new HttpGet(url); HttpResponse response = client.execute(get); System.out.println(response.getStatusLine()); HttpEntity entity = response.getEntity(); String result = dump(entity); get.abort(); return result; } /** * 执行登录过程 * * @param user * @param pwd * @param debug * @throws IOException */ static void login(String user, String pwd) throws IOException { HttpPost post = new HttpPost( "http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.3.14)"); post.setHeader("User-Agent", "Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0"); post.setHeader("Referer", "http://weibo.com/"); post.setHeader("Content-Type", "application/x-www-form-urlencoded"); // 登录表单的信息 List<NameValuePair> qparams = new ArrayList<NameValuePair>(); qparams.add(new BasicNameValuePair("entry", "miniblog")); qparams.add(new BasicNameValuePair("gateway", "1")); qparams.add(new BasicNameValuePair("from", "")); qparams.add(new BasicNameValuePair("savestate", "0")); qparams.add(new BasicNameValuePair("useticket", "1")); qparams.add(new BasicNameValuePair("ssosimplelogin", "1")); qparams.add(new BasicNameValuePair("service", "miniblog")); // servertime=1309164392 // nonce=PJZCHM // qparams.add(new BasicNameValuePair("pwencode", "wsse")); qparams.add(new BasicNameValuePair("encoding", "utf-8")); qparams.add(new BasicNameValuePair( "url", "http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack")); qparams.add(new BasicNameValuePair("returntype", "META")); qparams.add(new BasicNameValuePair("username", user)); qparams.add(new BasicNameValuePair("password", pwd)); UrlEncodedFormEntity params = new UrlEncodedFormEntity(qparams, "UTF-8"); post.setEntity(params); // Execute the request HttpResponse response = client.execute(post); post.abort(); // 新浪微博登录没有301,302之类的跳转;而是返回200,然后用javascript实现的跳转 // int statusCode = response.getStatusLine().getStatusCode(); // if ((statusCode == HttpStatus.SC_MOVED_PERMANENTLY) // || (statusCode == HttpStatus.SC_MOVED_TEMPORARILY) // || (statusCode == HttpStatus.SC_SEE_OTHER) // || (statusCode == HttpStatus.SC_TEMPORARY_REDIRECT)) { // // 此处重定向处理 此处还未验证 // String newUri = response.getLastHeader("Location").getValue(); // get(newUri); // } // Get hold of the response entity HttpEntity entity = response.getEntity(); // 取出跳转的url // location.replace("http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack&ticket=ST-MTkxODMxOTI0Nw==-1309224549-xd-263902F174B27BAB9699691BA866EFF2&retcode=0"); String location = getRedirectLocation(dump(entity)); get(location); } private static String getRedirectLocation(String content) { String regex = "location\\.replace\\(\'(.*?)\'\\)"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(content); String location = null; if (matcher.find()) { location = matcher.group(1); } return location; } /** * 打印页面 * * @param entity * @throws IOException */ private static String dump(HttpEntity entity) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader( entity.getContent(), "utf8")); //return EntityUtils.toString(entity); return IOUtils.toString(br); } public static void main(String[] args) throws IOException { login("username", "password"); String result = get("http://t.sina.com.cn/pub/tags"); System.out.println(result); } } |
3. Perl版
Perl版代码是鑫哥给我的,未知原始来源,同样也稍作了修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | use LWP::UserAgent; use HTTP::Cookies; use HTTP::Headers; my $ua = new LWP::UserAgent(keep_alive => 1); $ua->timeout(5); $ua->agent('Mozilla/4.0'); $ua->cookie_jar(HTTP::Cookies->new(file=>'getsina.cookies',autosave=>1)); my $res = $ua->post('http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.3.11)', [ service => 'miniblog', client => 'ssologin.js(v1.3.11)', entry => 'miniblog', encoding => 'utf-8', gateway => '1', savestate => '7', username => 'username', password => 'password', from => '', useticket => '0', url => 'http://t.sina.com.cn/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack', returntype => 'META', ], ); if (!$res->is_success) { print STDERR $res->status_line, "\n"; } my $req = HTTP::Request->new(GET => 'http://t.sina.com.cn/pub/tags'); $req->content_type('application/x-www-form-urlencoded'); $res = $ua->request($req); print $res->as_string; exit(0); |
你httpget这个地址:http://t.sina.com.cn/pub/tags没问题
但指向http://weibo.com/pub/tags时cookie被拒绝。。。就获取不到那个页面。他们两其实指向的是一个页面?怎么回事啊?
猜想 sina.com.cn 和 weibo.com 是两个不同的域,cookie一般情况下是不能共享的吧。建议你抓包看一下登录流程,自己分析一下吧,我在这方面一点都不懂。
我也非常困惑,其实单纯的登陆,可以由新浪会员那里登陆后再跳转到weibo.com的,但由前几天开始,有很多帐号跳转时就提示没有登陆了,但手工登录是没有问题的。也怀疑是cookie的问题,但不知道怎样解决。我是用VB的,楼主是否可指点一二,非常着急,这几天花了很多时间。我QQ8728609
我貌似也是同样的问题,交流下 啊
我对cookie一点都不懂,希望能有其他人能帮助到你。
昨天登的好好的,今天就显示cookie被拒绝了。。。。
真正的找不到方法,有人能解决吗
您好,这个问题愁死我了,你有解决方法么。
确实新浪进行很大的更新,cookie 有些被拒绝了. 无从下手,无法更新了.
有解决办法啊?
现在是不是所有账号都不能用了啊?
有解决方案么,愁死我了啊。
在login方法中最后get一个挑转的URL就可以了。非常感谢“冰糖葫芦”兄弟
panda,能告诉下具体方法吗?是get weibo.com吗?那登陆是怎样发包的呢?
panda兄,说的具体点啊。。。纠结好久了
代码已经更新,能够成功登录,请参考日志中的代码。
return IOUtils.toString(br);
这一句 报错了
楼主更新的代码运行良好~
想问下,楼主主要改进了什么地方啊?为什么要这么改啊?
改进主要在102行、103行:
String location = getRedirectLocation(dump(entity));
get(location);
是受了panda的留言的启发
楼主分析的很好。。
请问有登录新浪博客的方法吗?
登录新浪博客的方法大体上应该和日志中的登录新浪微博的方法是一致的,可能在一些细节上会有不同。你自己研究一下吧。
我用WebBrowser能登录成功,但是用户信息一直显示读取中。。。
太厉害了!
但是能不能解答下:password填的明文,发过去也能登陆成功,为什么呀? 传输过程中不是加密了吗?
这个具体要抓包看登录流程,要深入到javascript代码。我不懂javascript,无法回答你的问题。
怎么登陆不成功呢?
新浪的登录密码为暗码啊
[…] 转自:http://www.icycandy.com/blog/sina-microblog-login-using-httpclient-and-perl […]
现在还能运行啊????
现在貌似无法运行了,
String location = getRedirectLocation(dump(entity));返回为空。
修改为 String regex = “location\\.replace\\(\'(.*?)\’\\)”;
String location = getRedirectLocation(dump(entity));返回为空。
请问怎么解决? 前几天还能登录。
牛人!深受启发,已经登录微博成功了!