python爬虫:模拟登录新浪微博并实现cookies保存,详细讲一下rsa - Go语言中文社区

python爬虫:模拟登录新浪微博并实现cookies保存,详细讲一下rsa


前两天看了一篇大佬模拟登录淘宝的blog感觉收获颇多,刚好这两天比较闲,研究了下微博的登录方式

进入正题:

琢磨了两天找到了两种方式

  1. 构造登录时提交的加密参数,比较复杂,但是切换成不常用的ip也不会出验证码,这里详细讲这种方式
  2. 用selenium加载,切ip会出图形验证码,可以通过调用第三方API的方式破解,这里只写了不出验证码的情况供参考

第一种方式:

1.向’https://login.sina.com.cn/sso/prelogin.php’发送预登录的get请求
2.向’https://login.sina.com.cn/sso/login.php’发送正式登录的post请求
3.向’https://passport.weibo.com/wbsso/login?ticket=*****&ssosavestate=*****’发送get请求(****根据上一个请求响应而定,后面会说)
三步完成后进入登录状态,可以访问个人主页和做其他事情了~

  • 会用到RSA加密,不了解原理的可以看一下b站李永乐老师的视频,应该相当形象

这里是思路,可以直接跳过看代码

  1. chrome在未登录状态下打开微博,应该是这样的页面,先打开开发者模式,填写右边的用户名、密码,戳登录,页面跳转后,可以看到提交了几个POST请求(Preserve log记得勾选,否则一跳就看不到了)。最后一个明显是与登录相关,访问它不带参数的Request Url,可以到达一个比较纯净的登录接口: ‘https://login.sina.com.cn/signup/signin.php?entry=sso’在这里插入图片描述
    在这里插入图片描述
    2.同样打开开发者模式,用浏览器登录,提交的POST请求有两个,第一个是验证邮箱是否有效的,可以不用管;第二个就是我们的登录请求。提交的form data里有一堆参数。再重新登录一次,可以发现大部分是固定的,但是有几个参数值得注意,su, nonce, servertime, pubkey, rsakv, sp,构造出这几个参数,应该就可以顺利登录。
    在这里插入图片描述
    3.检索后,发现su参数是向’https://login.sina.com.cn/sso/prelogin.php’请求时带上去的,用charles或者fiddler等抓包软件可以看到这个请求返回了一个json文件,有nonce, servertime, pubkey, rsakv的值,与POST请求提交的一致,可以肯定在正式登录之前先请求了这个url
    在这里插入图片描述
    4.剩下su、sp没有解决,监听一下登陆页面的鼠标点击事件,发现一段关键代码,su参数就是由用户名经过url编码再base64转码后得来的。再往后走,sp参数也找到了,是登录密码经过rsa登录后得到的,sp详细的构造过程见代码注释
    在这里插入图片描述
    在这里插入图片描述
    5.所有参数都就绪了,但是发送正式登录请求返回的是一个json文件,CrossDomainUrlList里的正好是我们发送登录请求后要跳转的链接列表(在代码测试的时候会知道只有带ticket参数那个必须进行跳转在这里插入图片描述

**这里是代码:**
from time import time
import requests
import base64
from urllib import parse
import re
import json
import rsa
import binascii
from lxml import etree
import os

# 建立一个会话对象,使cookie可以跨请求保留
SESSION = requests.session()

# cookies保留再本地的路径
COOKIES_FILE_PATH = 'weibo_login_cookies.json'


class WeiboLogin:
    def __init__(self, username, password):
        """
        需传入两个参数
        :param username:用户名
        :param password: 密码
        """
        # 预登录url
        self.pre_login_url = 'https://login.sina.com.cn/sso/prelogin.php'
        # 正式登录url
        self.login_url = 'https://login.sina.com.cn/sso/login.php'
        # 个人主页url
        self.mysina_url = 'http://my.sina.com.cn/'

        # 微博用户名
        self.username = username
        # 微博密码
        self.password = password

    def per_login(self):
        """
        预登录,此步骤仅验证用户名
        :return:正式登录时要用上的5个参数值,su,nonce,servertime,pubkey,rsakv
        """
        # su参数是用户名经过url编码和base64转码后的字符串
        su = base64.b64encode(parse.quote(username).encode('utf-8')).decode('utf-8')
        per_login_params = {
            'entry': "account",
            'callback': "sinaSSOController.preloginCallBack",
            'su': su,
            'rsakt': "mod",
            'client': "ssologin.js(v1.4.15)",
            '_': int(time()*1000),
        }
        try:
            response = SESSION.get(self.pre_login_url, params=per_login_params)
            # 响应异常时,抛出异常信息
            response.raise_for_status()
        except Exception as error:
            print('预登录失败,原因:')
            raise error
        # 返回值是非标注格式的json字符串
        response_json = re.search(r'((.*?))', response.text).group(1)
        response_dict = json.loads(response_json)
        # 提取出需要的其他4个参数
        nonce = response_dict['nonce']
        servertime = response_dict['servertime']
        pubkey = response_dict['pubkey']
        rsakv = response_dict['rsakv']
        if nonce:
            print('预登录成功, nonce参数为: %s' % nonce)
        return su, nonce, servertime, pubkey, rsakv

    def encrypt_password(self, pubkey, servertime, nonce):
        """
        构造sp参数,sp参数是经过rsa加密的密码
        :params pubkey, servertime, nonce: 预登录取得
        :return: sp参数
        """
        # 接收方(微博服务器)提供的pubkey,转16进制
        rsa_publickey = int(pubkey, 16)
        # 加载公钥,传入参数n和参数e(关于参数的解释可参考这篇大佬文章 https://blog.csdn.net/chengqiuming/article/details/70139817)
        key = rsa.PublicKey(rsa_publickey, int('10001', 16))
        # 加载明文m
        message = str(servertime) + 't' + nonce + 'n' + password
        # 加密成密文c,有c = m^e mod n(解密的时候是还原m, m = c^d mod n, 其中d为私钥)
        encrypt_message = rsa.encrypt(message.encode('utf-8'), key)
        # 16进制密文
        sp = binascii.b2a_hex(encrypt_message).decode('utf-8')
        return sp

    def login(self):
        """
        构造form data发送post请求,正式登录
        :return: 登录成功的cookies
        """
        # 加载本地cookies文件,加载成功则跳过登录,直接访问个人主页
        if self.load_cookies():
            return True

        # cookies过期,正常进行登录
        login_params = {
            'client': "ssologin.js(v1.4.15)",
            '_': int(time() * 1000),
        }

        # 预登录,返回5个参数
        su, nonce, servertime, pubkey, rsakv = self.per_login()
        # sp参数
        sp = self.encrypt_password(pubkey, servertime, nonce)
        # 构造表单
        login_form_data = {
            'entry': "account",
            'gateway': "1",
            'from': "null",
            'savestate': "30",
            'useticket': "0",
            'vsnf': "1",
            'su': su,
            'service': "account",
            'servertime': servertime,
            'nonce': nonce,
            'pwencode': "rsa2",
            'rsakv': rsakv,
            'sp': sp,
            'sr': "1280*720",
            'encoding': "UTF-8",
            'cdult': "3",
            'domain': "sina.com.cn",
            'prelt': "170",
            'returntype': "TEXT",
        }
        login_url = 'https://login.sina.com.cn/sso/login.php'
        try:
            # 登录请求返回一个json文件,里面有三个url,验证第一个url必须跳转,否则无法成功访问个人主页
            response = SESSION.post(login_url, params=login_params, data=login_form_data)
            response.raise_for_status()
        except Exception as error:
            print('登陆失败,原因:')
            raise error

        # 做一次必要的跳转,跳转的目的是补全cookies信息
        cross_url = re.search(r'"(https:.*?)"', response.text).group(1)
        cross_url = re.sub(r'\', '', re.sub(r'\', '', cross_url))
        if cross_url:
            print('登录成功,正在跳转%s' % cross_url)
        # 发送get请求,使session自动更新cookies
        SESSION.get(cross_url)

        # 访问个人主页,成功则保存cookies至本地
        if self.verify_login_status():
            self.serialize_cookies()

    def verify_login_status(self):
        """
        访问个人主页,验证登录状态
        """
        try:
            response = SESSION.get(self.mysina_url)
            response.raise_for_status()
        except Exception as error:
            print('获取个人主页失败,原因: ')
            raise error
        # 不明原因返回了部分乱码,所以取content再decode
        html = etree.HTML(response.content.decode('utf-8'))
        nickname = html.xpath('//div[@class="me_w"]//p[@class="me_name"]/text()')[0]
        if nickname:
            print('已处于登录状态!当前处于[%s]的个人主页' % nickname)
            return True
        else:
            raise RuntimeError('获取微博昵称失败!response: %s' % response.content.decode('utf-8'))

    def load_cookies(self):
        """
        检查本地cookies是否可用
        """
        # 判断cookies文件是否存在
        if not os.path.exists(COOKIES_FILE_PATH):
            return False
        # 加载本地cookies
        SESSION.cookies = self.deserialize_cookies()
        # 判断cookies是否过期
        try:
            self.verify_login_status()
        except Exception as error:
            os.remove(COOKIES_FILE_PATH)
            print('本地cookies文件过期,正在删除...')
            return False
        print('加载新浪微博登录cookies成功!')
        return True

    def serialize_cookies(self):
        """
        保存cookies至本地
        """
        cookies_dict = requests.utils.dict_from_cookiejar(SESSION.cookies)
        with open(COOKIES_FILE_PATH, 'w+', encoding='utf-8') as file:
            json.dump(cookies_dict, file)
            print('保存cookies文件成功!文件名: %s' % COOKIES_FILE_PATH)

    def deserialize_cookies(self):
        """
        读取本地cookies
        """
        with open(COOKIES_FILE_PATH, 'r+', encoding='utf-8') as file:
            cookies_dict = json.load(file)
            cookies = requests.utils.cookiejar_from_dict(cookies_dict)
            return cookies


if __name__ == '__main__':
	# 这里输入用户名
    username = '*************'
    # 这里输入登录密码
    password = '*************'
    wb_login = WeiboLogin(username, password)
    wb_login.login()

程序会在根目录下创建登录成功的cookies文件
如果本地cookies未创建&已过期,运行结果如下:
在这里插入图片描述
如果本地cookies未过期,运行结果如下:
在这里插入图片描述


第二种方式:

selenium常规操作,
1.定位用户名、密码输入框,定位登录按钮
2.输入相应数据,点击登录按钮
3.登录成功(如果在非常用ip登录会弹出图形验证码,代码会报错,用第三方API识别验证码可破)


**这里是代码:**
from selenium import webdriver

# chrome driver所在路径
driver_path = r'C:Users12517PycharmProjectschromedriver.exe'
username = '*************'
password = '*************'

options = webdriver.ChromeOptions()
options.add_experimental_option('excludeSwitches', ['enable-automation'])
# 如果切换为非常用ip,页面会自动刷新出图形验证码(常用ip直接登陆即可成功)
driver = webdriver.Chrome(executable_path=driver_path, options=options)
driver.get('https://login.sina.com.cn/signup/signin.php')
username_tag = driver.find_element_by_xpath('//input[@id="username"]')
password_tag = driver.find_element_by_xpath('//input[@id="password"]')
login_btn = driver.find_element_by_xpath('//input[@class="W_btn_a btn_34px"]')
username_tag.send_keys(username)
password_tag.send_keys(password)
login_btn.click()
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/u011909077/article/details/100145910
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢