Python 模拟登录新浪微博的两种方法

最近在学习使用SAE,打算往上面放一个微博抢沙发的脚本,需要登录状态才能使用微博API。解决方法有两个,一是浏览器登录,手动复制cookie,二是模拟登录。我选择后者。

网上搜到一份教程,《python使用rsa加密算法模块模拟新浪微博登录》,非常复杂。对照着做的过程中,自己又找出一个简单的登录方法。两种方法都登录成功,记录如下:

本文涉及的Python版本:3.4

自己找出来的简单方法

登录页:http://login.sina.com.cn/signup/signin.php?entry=sso

抓包可找到登录网址:

https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.15/)&_=1448285708709

最后一项是时间戳,经测试,可以直接删除。所以最终登录网址是:

https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.15)

POST提交的表单中,用户名需要base64编码。
完整登录代码如下:

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
import requests
import json
import base64

def login(username, password):
username = base64.b64encode(username.encode('utf-8')).decode('utf-8')
postData = {
"entry": "sso",
"gateway": "1",
"from": "null",
"savestate": "30",
"useticket": "0",
"pagerefer": "",
"vsnf": "1",
"su": username,
"service": "sso",
"sp": password,
"sr": "1440*900",
"encoding": "UTF-8",
"cdult": "3",
"domain": "sina.com.cn",
"prelt": "0",
"returntype": "TEXT",
}
loginURL = r'https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.15)'
session = requests.Session()
res = session.post(loginURL, data = postData)
jsonStr = res.content.decode('gbk')
info = json.loads(jsonStr)
if info["retcode"] == "0":
print("登录成功")
# 把cookies添加到headers中,必须写这一步,否则后面调用API失败
cookies = session.cookies.get_dict()
cookies = [key + "=" + value for key, value in cookies.items()]
cookies = "; ".join(cookies)
session.headers["cookie"] = cookies
else:
print("登录失败,原因: %s" % info["reason"])
return session

if __name__ == '__main__':
session = login('你的用户名', '你的密码')

返回的session保存了登录状态,尽情做你做想做的事吧,配合微博API效果更佳。


网上搜索到的复杂方法

本节依赖的第三方库除了上面用过的requests,还有用于加密的rsa,需要另行安装。

登录流程

  1. 向如下网址提交GET请求,获取servertim、nonce、pubkey、rsakv,后两者是固定值。
    http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=&rsakt=mod&client=ssologin.js(v1.4.18))
  2. 加密用户名与密码
  3. 向如下网址提交POST请求:
    http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.18))

加密方式的破解找到一段方法,我没学过javascript,就没细看,直接照搬后面的加密方法。

获得以及查看新浪微博登录js文件:
查看新浪通行证url(http://login.sina.com.cn/signup/signin.php)的源代码,其中可以找到该js的地址http://login.sina.com.cn/js/sso/ssologin.js,不过打开后里面的内容是加密过的,可以在网上找个在线解密站点解密,查看最终用户名和密码的加密方式。

分步实现

获取servertim和nonce

直接在浏览器中访问:
http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=&rsakt=mod&client=ssologin.js(v1.4.18))

未登录状态返回:

sinaSSOController.preloginCallBack({
    "retcode": 0,
    "servertime": 1448278360,
    "pcid": "gz-8139fa256b150b7a42e09dbdc0b4ed2224eb",
    "nonce": "RDY5SN",
    "pubkey": "EB2A38568661887FA180BDDB5CABD5F21C7BFD59C090CB2D245A87AC253062882729293E5506350508E7F9AA3BB77F4333231490F915F6D63C55FE2F08A49B353F444AD3993CACC02DB784ABBB8E42A9B1BBFFFB38BE18D78E87A0E41B9B8F73A928EE0CCEE1F6739884B9777E4FE9E88A1BBE495927AC4A799B3181D6442443",
    "rsakv": "1330428213",
    "exectime": 9
})

取出括号里的JSON字符串提取信息:

1
2
3
4
5
6
7
8
9
10
def getLoginInfo():
preLoginURL = r'http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=&rsakt=mod&client=ssologin.js(v1.4.18)'
html = requests.get(preLoginURL).text
jsonStr = re.findall(r'\((\{.*?\})\)', html)[0]
data = json.loads(jsonStr)
servertime = data["servertime"]
nonce = data["nonce"]
pubkey = data["pubkey"]
rsakv = data["rsakv"]
return servertime, nonce, pubkey, rsakv

加密用户名与密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def getSu(username):
"""加密用户名,su为POST中的用户名字段"""
su = base64.b64encode(username.encode('utf-8')).decode('utf-8')
return su

def getSp(password, servertime, nonce, pubkey):
"""加密密码,sp为POST中的用户名字段"""
pubkey = int(pubkey, 16)
# 65537是js加密文件文件中的固定值,原是十六进制数字10001
key = rsa.PublicKey(pubkey, 65537)
# 以下拼接明文从js加密文件中得到
message = str(servertime) + '\t' + str(nonce) + '\n' + str(password)
message = message.encode('utf-8')
sp = rsa.encrypt(message, key)
# 把二进制数据的每个字节转换成相应的2位十六进制表示形式。
sp = binascii.b2a_hex(sp)
return sp

提交POST请求

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
def login(su, sp, servertime, nonce, rsakv):
postData = {
'entry': 'weibo',
'gateway': '1',
'from': '',
'savestate': '7',
'userticket': '1',
"pagerefer": "http://open.weibo.com/wiki/2/statuses/home_timeline",
"vsnf": "1",
"su": su,
"service": "miniblog",
"servertime": servertime,
"nonce": nonce,
"pwencode": "rsa2",
"rsakv": rsakv,
"sp": sp,
"sr": "1440*900",
"encoding": "UTF-8",
"prelt": "126",
"url": "http://open.weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack",
"returntype": "META",
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "login.sina.com.cn",
"Origin": "http://open.weibo.com",
"Referer": "http://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6%E8%AF%B4%E6%98%8E",
"Upgrade-Insecure-Requests": "1",
}
loginURL = r'http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.18)'
session = requests.Session()
session.headers = headers
res = session.post(loginURL, data=postData)
html = res.content.decode('gbk')
info = re.findall(r"location\.replace\(\'(.*?)\'", html)[0]
if 'retcode=0' in info:
print("登录成功!")
else:
print("登录失败!")
return session

完整代码

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
import requests
import json
import re
import base64
import rsa
import binascii


def getLoginInfo():
preLoginURL = r'http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=&rsakt=mod&client=ssologin.js(v1.4.18)'
html = requests.get(preLoginURL).text
jsonStr = re.findall(r'\((\{.*?\})\)', html)[0]
data = json.loads(jsonStr)
servertime = data["servertime"]
nonce = data["nonce"]
pubkey = data["pubkey"]
rsakv = data["rsakv"]
return servertime, nonce, pubkey, rsakv


def getSu(username):
"""加密用户名,su为POST中的用户名字段"""
su = base64.b64encode(username.encode('utf-8')).decode('utf-8')
return su




def getSp(password, servertime, nonce, pubkey):

"""加密密码,sp为POST中的用户名字段"""
pubkey = int(pubkey, 16)
key = rsa.PublicKey(pubkey, 65537)
# 以下拼接明文从js加密文件中得到
message = str(servertime) + '\t' + str(nonce) + '\n' + str(password)
message = message.encode('utf-8')
sp = rsa.encrypt(message, key)
# 把二进制数据的每个字节转换成相应的2位十六进制表示形式。
sp = binascii.b2a_hex(sp)
return sp

def login(su, sp, servertime, nonce, rsakv):
postData = {
'entry': 'weibo',
'gateway': '1',
'from': '',
'savestate': '7',
'userticket': '1',
"pagerefer": "http://open.weibo.com/wiki/2/statuses/home_timeline",
"vsnf": "1",
"su": su,
"service": "miniblog",
"servertime": servertime,
"nonce": nonce,
"pwencode": "rsa2",
"rsakv": rsakv,
"sp": sp,
"sr": "1440*900",
"encoding": "UTF-8",
"prelt": "126",
"url": "http://open.weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack",
"returntype": "META",
}
loginURL = r'http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.18)'
session = requests.Session()
res = session.post(loginURL, data=postData)
html = res.content.decode('gbk')
info = re.findall(r"location\.replace\(\'(.*?)\'", html)[0]
if 'retcode=0' in info:
print("登录成功!")
else:
print("登录失败!")
return session

if __name__ == '__main__':
servertime, nonce, pubkey, rsakv = getLoginInfo()
su = getSu("你的用户名")
sp = getSp("你的密码", servertime, nonce, pubkey)
session = login(su, sp, servertime, nonce, rsakv)
loveNight wechat
我的微信公众号,放一些有趣的内容,不定期更新。