思路
对于爬虫来说,模拟登陆成功=获取可以直接使用的cookie,这样就可以告诉服务器你属于登陆状态然后直接开始访问(抓取)了。
新浪微博登陆的过程分几步,唯一让我觉得意外的是尽然没有涉及到验证码。
prelogin
在你输入完用户名密码后,浏览器会发送一个prelogin请求,内容如下:
https://login.sina.com.cn/sso/prelogin.php?
entry=account&
callback=pluginSSOController.preloginCallBack&
su=d2ZneWRidSU0MHNpbmEuY29t&
rsakt=mod&
checkpin=1&
client=ssologin.js(v1.4.19)&
_=1527201605254
这里面几乎值都可以直接搬运,su
是url编码+base64编码后的用户名,_
是当前时间。该请求会返回一个名为pluginSSOController.preloginCallBack
字典,其中就包括后面要用到的servertime
, nonce
, pubkey
和rsakv
。
login
正式login的请求是这样的:
https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.19)&_=1527201708515
重点是它所带的FormData:
entry: account
gateway: 1
from:
savestate: 30
qrcode_flag: true
useticket: 0
pagerefer: http://my.sina.com.cn/profile/logined
vsnf: 1
su: ####
service: sso
servertime: ####
nonce: ####
pwencode: rsa2
rsakv: ####
sp: ####
sr: 1920*1080
encoding: UTF-8
cdult: 3
domain: sina.com.cn
prelt: 390
returntype: TEXT
其中su
,servertime
, nonce
和rsakv
可以直接拿来用,sp
需要自己计算,其他的照搬就行。
计算sp
如果你能找到ssologin.js
这事就成了百分之八十了。新浪微博的密码采用的是RSA加密(也就是公钥私钥模式),上面获得的pubkey
就是它的公钥。要加密的消息是这样的:
servertime'\t'nonce'\n'
password
我这里采取的方法是使用execjs
模块调用js代码,将ssologin.js
保存下来并在末尾添加两个方法:
function get_encrypted_name(name){
return sinaSSOEncoder.base64.encode(encodeURIComponent(name));
}
function get_encrypted_password(password,nonce,servertime,rsaPubkey){
var RSAKey = new sinaSSOEncoder.RSAKey();
RSAKey.setPublic(rsaPubkey, "10001");
return RSAKey.encrypt([servertime, nonce].join("\t") + "\n" + password)
}
get_encrypted_name()
返回加密后的用户名,get_encrypted_password()
返回加密后密码。
获取cookie
在login请求成功后,会返回一个字典里面包含一个名为crossDomainUrlList
的字段,里面每个URL都会返回一部分cookie,有的是重复的,没关系。将这些cookie全部收集起来,就是我们最后需要使用的Cookie。
代码
getCookie.py
import requests
import os
import time
import re
import ast
import execjs
from random import choice
import Configure
header = {}
header['user-agent'] = choice(Configure.FakeUserAgents)
def __pre_login(username_base64):
url = "http://login.sina.com.cn/sso/prelogin.php"
payload = {
'entry':'account',
'callback':'pluginSSOController.preloginCallBack',
'su':username_base64,
'rsakt':'mod',
'checkpin':1,
'client':'ssologin.js(v1.4.19)',
'_':int(time.time()*1000)
}
try:
response = requests.get(url, headers=header, params=payload)
content = None
if response.status_code == requests.codes.ok:
content = response.text
except Exception as e:
print (e)
pattern = re.compile('pluginSSOController.preloginCallBack\((.*?)\)', re.S)
data_dict = pattern.findall(content)[0]
return data_dict
def get_cookie():
# get js ready
phantom = execjs.get('PhantomJS')
with open('ssologin.js','r', encoding='utf-8') as file:
source = file.read()
ctx = phantom.compile(source)
username_base64 = ctx.call('get_encrypted_name',Configure.susername)
pre_login_dic = __pre_login(username_base64)
# pre_login_dic 只是字符串,转化为字典
pre_login_dic = ast.literal_eval(pre_login_dic)
# start login process
servertime = pre_login_dic.get('servertime')
nonce = pre_login_dic.get('nonce')
pubkey = pre_login_dic.get('pubkey')
rsakv = pre_login_dic.get('rsakv')
sp = ctx.call('get_encrypted_password',Configure.spassword, nonce, servertime, pubkey)
login_url = 'http://login.sina.com.cn/sso/login.php'
payload = {
'client':'ssologin.js(v1.4.19)',
'_':int(time.time()*1000)
}
formdata = {
'entry': 'account',
'gateway': '1',
'from': '',
'savestate': '30',
'qrcode_flag': 'true',
'useticket': '1',
'pagerefer':'http://my.sina.com.cn/profile/logined',
'vsnf':'1',
'su': username_base64,
'service':'sso',
'servertime': servertime,
'nonce': nonce,
'pwencode': 'rsa2',
'rsakv' : rsakv,
'sp': sp,
'sr': '1920*1080',
'encoding': 'UTF-8',
'cdult':3,
'domain':'sina.com.cn',
'prelt':518,
'returntype': 'TEXT' # 'META'
}
try:
response = requests.post(login_url, headers=header, params=payload, data=formdata)
content = None
#print (response.data)
if response.status_code == requests.codes.ok:
content = response.text
except Exception as e:
print (e)
#发送get请求并保存cookies
data = ast.literal_eval(content.replace('\\',''))
crossDomainUrls = data.get('crossDomainUrlList')
final_cookies = []
for url in crossDomainUrls:
try:
response = requests.get(url, headers=header)
content = None
if response.status_code == requests.codes.ok:
cookies = response.cookies
except Exception as e:
print (e)
cookie_dict = cookies.get_dict()
for cookie in cookie_dict:
final_cookies.append("{0:s}={1:s}".format(cookie, cookie_dict.get(cookie)))
connector = ';'
return connector.join(final_cookies)
if __name__ == '__main__':
get_cookie()