攻克微博(1) - 模拟微博登陆

思路

对于爬虫来说,模拟登陆成功=获取可以直接使用的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, pubkeyrsakv

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, noncersakv 可以直接拿来用,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()

你可能感兴趣的:(攻克微博(1) - 模拟微博登陆)