python爬蟲之新浪微博登陸

fiddler 以前瞭解了一些常見到的反爬措施,JS加密算是比較困難,而微博的登陸中正是用JS加密來反爬,今天來了解一下。php

分析過程

首先咱們去抓包,從登陸到微博首頁加載出來的過程。咱們重點關注一下登陸操做,其次是首頁的請求,登陸通常是POST請求。咱們搜索一下:html

 得知登陸的url爲https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.19),而後點擊WebForms菜單查看參數:python

裏面有不少參數要提交,通常的參數的值有3種狀況:ajax

  1. 參數值固定。通常咱們屢次抓包發現某個參數值不變,就認爲是固定的;
  2. 參數值來自於以前服務器的響應。屢次抓包發現參數值變化,此時咱們能夠把參數的值在fiddler中查找一下,看看可否在以前的響應中找到。例如這裏的nonce、rsakv、servertime
  3. 參數值來自於js生成。若是屢次抓包參數的值既不是固定,也不能在以前的響應中找到,那麼最可能的結果是這個參數的值是由js代碼生成。

咱們在fiddler中查找nonce:服務器

發現有一個前面的請求高亮了,說明這個參數以前就出現過。點擊這個請求,在響應裏查找這個值:session

能夠找到這個參數,因此咱們要想登陸,就得獲取nonce的值,而要獲取nonce的值,就要先請求這個找到的請求,這個請求的url爲https://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=MTgzMTI0OTMxMDc%3D&rsakt=mod&checkpin=1&client=ssologin.js(v1.4.19)&_=1533119627438,url中的su在後面會講到,最後那個參數看起來像時間戳,咱們能夠先用時間戳模擬一下。而servertime、rsakv等必須的參數也均可以在這個響應中找到。dom

如今咱們解決了大部分參數的問題,可是有兩個難啃的骨頭:sp和su,這兩個值在以前的響應中找不到。並且咱們會發現,咱們登陸輸入的帳號和密碼沒有出如今這些參數中,咱們大膽猜想:su和sp就是帳號和密碼!那麼咱們怎麼找到它們的值呢。答案是找到相應的JS代碼,並用python重寫它。函數

如今咱們的難題到了怎麼定位這倆個值的JS代碼,咱們以前學習Chrome調試的時候,學會了打斷點,這個地方正是用斷點的方式來找。咱們每次登陸都要點擊頁面的「登陸」按鈕,咱們在填寫完帳號密碼後,設置一個點擊事件的斷點,而後點擊登陸。這樣請求在進行登陸的時候會暫停,而su和sp參數也是這個時候被加密!post

而後,咱們用調試界面右上角的這些功能鍵進行逐步分析。學習

注意:通常只是賦值的操做,咱們能夠跳過,若是是函數的執行,咱們要到函數裏面去看,特別是函數的參數是攜帶重要參數,要重點關注。在控制檯界面,咱們還能夠查看某些參數的值。

另外,若是退出某個函數後,光標仍在這一行,說明這一行還有個函數,切不可直接下一步,不少關鍵信息就在這個函數裏。

微博登陸的JS定位過程就不細說了,咱們最後定位到su和sp加密代碼以下:

即su是用base64進行了編碼,而sp是用rsa加密,咱們把js代碼用python代碼實現便可。

目前,登陸的問題解決了。如今看看請求首頁的問題。咱們逐個查看,能夠知道首頁的請求以下:

url爲https://weibo.com/u/6505689778/home?wvr=5&lf=reg,而這個url裏有個6505689778,這個值咱們在fiddler中查找,在請求https://passport.weibo.com/wbsso/login?ticket=ST-NjUwNTY4OTc3OA%3D%3D-1533119623-gz-0DEAF5775E6F1D983147B0B96EE915B9-1&ssosavestate=1564655623&callback=sinaSSOController.doCrossDomainCallBack&scriptId=ssoscript0&client=ssologin.js(v1.4.19)&_=1533119634900的響應裏能找到它的。

而請求這個頁面,又要獲取參數ticket、ssosavestate的值,咱們再次查找,能夠知道這兩個值在另外一個請求https://login.sina.com.cn/crossdomain2.php?action=login&entry=weibo&r=https%3A%2F%2Fpassport.weibo.com%2Fwbsso%2Flogin%3Fssosavestate%3D1564655623%26url%3Dhttps%253A%252F%252Fweibo.com%252Fajaxlogin.php%253Fframelogin%253D1%2526callback%253Dparent.sinaSSOController.feedBackUrlCallBack%2526sudaref%253Dweibo.com%26display%3D0%26ticket%3DST-NjUwNTY4OTc3OA%3D%3D-1533119623-gz-39B6B6D3D3979D6DA2860B54E4E61A01-1%26retcode%3D0&login_time=1533119622&sign=0db5e9f42ceb691c&sr=1536%2A864的響應裏。

那麼這個很長的url怎麼來的呢,咱們再次查找,能夠得知它就在登陸以後響應裏。

到了這裏,步驟已經走通了!

咱們理一下步驟:

一、先把帳號、密碼加密後的密文獲得

二、請求https://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=MTgzMTI0OTMxMDc%3D&rsakt=mod&checkpin=1&client=ssologin.js(v1.4.19)&_=1533119627438獲得nonce、rsakv等參數

三、構造參數並請求登陸的url:https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.19),在響應裏獲得跳轉的url

四、請求跳轉的url:https://login.sina.com.cn/crossdomain2.php?action=login&entry=weibo&r=https%3A%2F%2Fpassport.weibo.com%2Fwbsso%2Flogin%3Fssosavestate%3D1564655623%26url%3Dhttps%253A%252F%252Fweibo.com%252Fajaxlogin.php%253Fframelogin%253D1%2526callback%253Dparent.sinaSSOController.feedBackUrlCallBack%2526sudaref%253Dweibo.com%26display%3D0%26ticket%3DST-NjUwNTY4OTc3OA%3D%3D-1533119623-gz-39B6B6D3D3979D6DA2860B54E4E61A01-1%26retcode%3D0&login_time=1533119622&sign=0db5e9f42ceb691c&sr=1536%2A864,獲得ticket、ssosavestate參數的值

五、請求https://passport.weibo.com/wbsso/login?ticket=ST-NjUwNTY4OTc3OA%3D%3D-1533119623-gz-0DEAF5775E6F1D983147B0B96EE915B9-1&ssosavestate=1564655623&callback=sinaSSOController.doCrossDomainCallBack&scriptId=ssoscript0&client=ssologin.js(v1.4.19)&_=1533119634900獲得uniqueid參數

六、請求首頁:https://weibo.com/u/6505689778/home?wvr=5&lf=reg

OK,至此,咱們已成功登陸了微博,後面你要獲取微博上的數據,能夠自行請求。

實現代碼

import requests
import rsa
import time
import re
import random
import urllib3
import base64
from urllib.parse import quote
from binascii import b2a_hex
urllib3.disable_warnings() # 取消警告

def get_timestamp():
    return int(time.time()*1000)  # 獲取13位時間戳

class WeiBo():
    def __init__(self,username,password):
        self.username = username
        self.password = password
        self.session = requests.session() #登陸用session
        self.session.headers={
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        }
        self.session.verify = False  # 取消證書驗證

    def prelogin(self):
        '''預登陸,獲取一些必須的參數'''
        self.su = base64.b64encode(self.username.encode())  #閱讀js得知用戶名進行base64轉碼
        url = 'https://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su={}&rsakt=mod&checkpin=1&client=ssologin.js(v1.4.19)&_={}'.format(quote(self.su),get_timestamp()) #注意su要進行quote轉碼
        response = self.session.get(url).content.decode()
        # print(response)
        self.nonce = re.findall(r'"nonce":"(.*?)"',response)[0]
        self.pubkey = re.findall(r'"pubkey":"(.*?)"',response)[0]
        self.rsakv = re.findall(r'"rsakv":"(.*?)"',response)[0]
        self.servertime = re.findall(r'"servertime":(.*?),',response)[0]
        return self.nonce,self.pubkey,self.rsakv,self.servertime

    def get_sp(self):
        '''用rsa對明文密碼進行加密,加密規則經過閱讀js代碼得知'''
        publickey = rsa.PublicKey(int(self.pubkey,16),int('10001',16))
        message = str(self.servertime) + '\t' + str(self.nonce) + '\n' + str(self.password)
        self.sp = rsa.encrypt(message.encode(),publickey)
        return b2a_hex(self.sp)

    def login(self):
        url = 'https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.19)'
        data = {
        'entry': 'weibo',
        'gateway': '1',
        'from':'',
        'savestate': '7',
        'qrcode_flag': 'false',
        'useticket': '1',
        'pagerefer': 'https://login.sina.com.cn/crossdomain2.php?action=logout&r=https%3A%2F%2Fweibo.com%2Flogout.php%3Fbackurl%3D%252F',
        'vsnf': '1',
        'su': self.su,
        'service': 'miniblog',
        'servertime': str(int(self.servertime)+random.randint(1,20)),
        'nonce': self.nonce,
        'pwencode': 'rsa2',
        'rsakv': self.rsakv,
        'sp': self.get_sp(),
        'sr': '1536 * 864',
        'encoding': 'UTF - 8',
        'prelt': '35',
        'url': 'https://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack',
        'returntype': 'META',
        }
        response = self.session.post(url,data=data,allow_redirects=False).text # 提交帳號密碼等參數
        redirect_url = re.findall(r'location.replace\("(.*?)"\);',response)[0] # 微博在提交數據後會跳轉,此處獲取跳轉的url
        result = self.session.get(redirect_url,allow_redirects=False).text  # 請求跳轉頁面
        ticket,ssosavestate = re.findall(r'ticket=(.*?)&ssosavestate=(.*?)"',result)[0] #獲取ticket和ssosavestate參數
        uid_url = 'https://passport.weibo.com/wbsso/login?ticket={}&ssosavestate={}&callback=sinaSSOController.doCrossDomainCallBack&scriptId=ssoscript0&client=ssologin.js(v1.4.19)&_={}'.format(ticket,ssosavestate,get_timestamp())
        data = self.session.get(uid_url).text #請求獲取uid
        uid = re.findall(r'"uniqueid":"(.*?)"',data)[0]
        print(uid)
        home_url = 'https://weibo.com/u/{}/home?wvr=5&lf=reg'.format(uid) #請求首頁
        html = self.session.get(home_url)
        html.encoding = 'utf-8'
        print(html.text)

    def main(self):
        self.prelogin()
        self.get_sp()
        self.login()

if __name__ == '__main__':
    username = 'xxxxxxxxx' # 微博帳號
    password = 'xxxxxxxxx' # 微博密碼
    weibo = WeiBo(username,password)
    weibo.main()

結果:

相關文章
相關標籤/搜索