【原创】转载请注明,来自:DigDeeply的博客: http://digdeeply.org/

这几天晚上闲着没事干,偶然间发现新浪爱问的积分还可以换实物礼品,积分来源一部分就是提问回答之类的,还有一部分是要每天签到。我是懒人,也懒得每天提问回答啥的,为了那么个小礼品,也不值得。但是签到还是很简单的嘛,每天点一下就OK了。我比一般懒人还懒,每天点一下都懒得点,所以觉定做个模拟登陆,模拟签到的程序,放到SAE上去,每天帮我签个到,拿个积分就好了,哈哈。:)

其实这个模拟登陆,也就是抓站的基础部分吧, 模拟登陆,获取登陆后的状态,然后模拟人的访问流程,获取访问结果,分析保存之……

首先介绍一下抓站的利器吧,httpwatch,也可以用firefox下的firebug或者chrome下自带的开发者工具,其实我原来一直都是用firefox或者chrome的,但是听说httpwatch很好,就拿来试用一下,结果用的还真是很舒服,不错。其次就是Snoopy.class.php,我用PHP,这个PHP类真是好用到极致了,用来抓站so easy。但是这个类用来抓一般的基于http的站是没问题的,如果要是https的就麻烦了,这个类在抓https的站时,采用的不是php本身的curl了,使用的是Linux/Unix下的原生CURL,所以这个类在windows下用着很麻烦,需要自己装一个windows下能用的curl,并且配置好环境。在sae下直接就是不行的了,如果是用Linux/Unix的话,就很方便了~~指定好curl的路径就可以了。我也有一个https的模拟登录签到的脚本,使用的是php的curl,没有使用Snoopy。

由于新浪采用了单点登陆,所以一般的登陆方法是不行的了,不是直接构造用户名密码POST到一个login action页面完事。先使用httpwatch抓取一下登陆的流程吧。

从这个流程中,我们仔细分析分析,就会发现,整个登陆的流程是这样的:

1)输入用户名密码,点击登陆(在用户名输入完onblur的时候会有一个自动检测邮箱合法性的过程,我们模拟时候可以忽略)。

2)访问一个页面,获取到几个特殊的值,包括servertime,pcid,nonce,这几个值用来干什么的呢,我们再分析一下JS就可以发现了,是用来加密用户名和密码的。

3)将加密后的用户名、密码以及一些其它信息,提交到SSO的login去申请ticket。(ticket就是SSO登陆中用到的票据啦)

4)认证成功,访问几个其它站点种下Cookie,(相当于把票给人家看门的看看,告诉他我有访问你内部的权限了)。

5)返回爱问首页。

这个流程弄明白了,也就没什么难度了。主要需要做的就是如何实现用户名和密码的加密,因为客户端是用js实现的,但是我们是写在脚本里的,无法调用js,所以,只能通过PHP来模拟一下js加密的实现过程了。

新浪的JS采用的应该是Dean Edwards的packer算法,其实不用管什么算来,拿来之后在Google一下js解密,放进去就是还原后的代码了。

在还原后,大概看了看,就明白了一个流程,用户名和密码都分别进行了加密,最重要的部分,就是加密的这块了。用户名用base64加密,密码用hex_sha1加密后加密再加盐再加密。我们现在需要做的,就是用PHP来实现这两个加密方法。(其实通过分析后发现,用户名采用base64加密,并未加盐,所以每次加密后的数据都是一样的,我们也没必要去实现base64的加密方法了。)

最后通过努力,把这个JS的对象封装成了一个PHP的类,具体的代码我就不贴出来了,好歹我也是浪人,自己就不危害自己啦,大家自己研究下,很简单的。

其中最难的亮点就是js中的 »> 无符号右移 和 charCodeAt(i),PHP中没有这两个对应的实现,需要自己来写。

我把这两个难点贴出来供大家参考吧,其实这两个算法我也是参考的别人的,中间发现有一个算法是错的,浪费了我好长时间。。。。。

就是这两个算法了,还有一个无符号左移的,这里没用到,也贴下来,记录。

还有一个需要注意的是PHP和JS中的三元判断符的使用,PHP中是从右向左,JS中是从左向右。所以这个使用if…else…替代比较好。

还有一个就是JS的数组和PHP的数组问题,自己想办法解决吧。

解决了这个,剩下就没难题了,跟普通的抓站一样了。构造好需要的POST数据,提交。然后获取返回的ticket,然后再模拟访问其它几个页面,把ticket给人家看门的看一眼,下次带Cookie直接访问里面就好了。