很久之前就了解过模拟登录的过程,最近对python用的比较多,想来练练手,就想实现一下新浪微博登录,首先随便一搜,网上有大量的前辈们都做过了,我也仔细看了一下,并且参考之后发现无法登录,而且还有很多细节没有说得太清楚。同时网上最新的也是很久之前的,对于最新的版本也有一些改动,因此将我接近两天时间的研究全过程记录一下。
已有实现的简要过程
网上已有实现可以见http://www.douban.com/note/201767245/以及http://www.jb51.net/article/44779.htm。当然还有很多其他例子,都是比较早前的实现,总体来说大致过程都分为如下三步:
1 .预登陆
通过请求http://login.sina.com.cn/sso/prelogin.php来获取客户端对用户密码进行加密的参数。主要列举如下:
- pubkey :客户端使用RSA加密的公钥
- servertime:服务器时间,用来与用户密码一起扰乱加密
- nonce:服务器随机字符串,用来与用户密码一起扰乱加密
2 .登录
通过http://login.sina.com.cn/sso/login.php?client=ssologin.js使用POST对用户名密码进行处理,以及其他相关参数的处理后,得到这个请求返回的cookie和正文html内容。
主要涉及到的是用户名先使用urlencode加密然后base64加密,密码使用如下方式扰乱:
servertime + '\t' + nonce + '\n' + password
然后对扰乱后的字符串使用RSA加密。
3 .跳转到ajaxlogin
将第二部中得到的正文html内容中的一段JavaScript代码中的“location.replace()”中的地址用正则取出,然后对这个地址发起访问就结束了。
上述三个步骤就是目前已有的模拟登录的简要概述,但是目前微博已经做了一些改动,有很多细节需要注意,详细记录如下。所有实现封装在一个类中,详细步骤的代码片段都在这个环境下。
prelogin请求
这个请求在用户输入微博名称之后,选中用户密码输入框时,会使用ajax方式请求一遍,获取用户输入的微博登录名实时对应的随机信息。请求参数如下:
entry:weibo
callback:sinaSSOController.preloginCallBack
su:base64.encode(urlencode(username))
rsakt:mod
checkpin:1
client:ssologin.js(v1.4.18)
_:1437133246747
其中可以看出ssologin.js的版本已经是1.4.18了,比之前列出的版本都更新很多版本了。最后一个参数是客户端的时间,单位是毫秒。以上通过fiddler获取。上述python实现如下:
def __mtime(self):
'''Return the current time by milli-second'''
return long('%.0f' % (time.time() * 1000))
b64username = base64.b64encode(urllib.quote(self.username))
preReqHeader = {
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Encoding':'gzip, deflate',
'Accept-Language':'zh-CN,zh;q=0.8',
'Cache-Control':'max-age=0',
'Connection':'keep-alive',
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36',
'Host':'login.sina.com.cn',
'Origin':'http://weibo.com',
'Referer':'http://weibo.com/',
}
plt = self.__mtime()
payload = {
'entry':'weibo',
'callback':'sinaSSOController.preloginCallBack',
'su': b64username,
'rsakt':'mod',
'checkpin':'1',
'client':'ssologin.js(v1.4.18)',
'_': plt,
}
上述请求返回了一段JavaScript代码:
sinaSSOController.preloginCallBack({
"retcode":0,"servertime":1437133178,"pcid":"xd-b0fc6894be2638ae76e6399c104101c9d433","nonce":"SVRB9M","pubkey":"EB2A38568661887FA180BDDB5CABD5F21C7BFD59C090CB2D245A87AC253062882729293E5506350508E7F9AA3BB77F4333231490F915F6D63C55FE2F08A49B353F444AD3993CACC02DB784ABBB8E42A9B1BBFFFB38BE18D78E87A0E41B9B8F73A928EE0CCEE1F6739884B9777E4FE9E88A1BBE495927AC4A799B3181D6442443","rsakv":"1330428213","showpin":0,"exectime":9})
这里使用正则方式提取了preloginCallBack函数中的对象,并转换为python字典对象。完整请求封装为一个函数:
def preLogin