上一節(jié),我們講了如何配置Charles代理,這一節(jié)我們通過模擬微博登錄這個(gè)例子來看看如何使用Charles分析網(wǎng)站加載流程,順便把微博模擬登錄的Python代碼也給實(shí)現(xiàn)了。
首先,我們運(yùn)行Charles并開始記錄。然后打開Chrome瀏覽器,選擇使用Charles代理,打開微博首頁(yè) ,出現(xiàn)登錄頁(yè)面(如果之前登錄過微博,要先退出登錄)。輸入用戶名和密碼進(jìn)行登錄,登錄成功后就可以停止Charles的記錄。這樣我們就用Charles完整記錄下了微博的登錄過程。見圖:
我們把整個(gè)登錄過程寫出一個(gè)Python類,它的定義為:
接下來我們分析登錄過程,并逐一實(shí)現(xiàn)這個(gè)類的各個(gè)方法。
把Charles的主窗口切換到“Sequence”標(biāo)簽頁(yè),
我們可以按加載時(shí)間順序觀察Charles記錄的微博登錄過程,我們發(fā)現(xiàn)第一個(gè)可疑的請(qǐng)求的Host是:
點(diǎn)擊該條記錄,下方出現(xiàn)該條請(qǐng)求的完整內(nèi)容,它的路徑是:
GET /sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=&rsakt=mod&client=ssologin.js(v1.4.19)&_=1542456042531 HTTP/1.1
這個(gè)GET請(qǐng)求的參數(shù)_=1542456042531看起來是個(gè)時(shí)間戳,這個(gè)在ssologin.js(看后面是如何找到的)定義為preloginTimeStart,可以用int(time.time()*1000)得到。
從prelogin.php這個(gè)名字看,它是一個(gè)預(yù)登陸,即在你輸入用戶名和密碼前,它先從服務(wù)器拿點(diǎn)東西過來:
用Python實(shí)現(xiàn)這個(gè)prelogin:
補(bǔ)充:關(guān)于認(rèn)證碼
昨天最初寫這篇教程的時(shí)候,沒有碰到驗(yàn)證碼。今天就碰到驗(yàn)證碼跳出來了,真是大快人心,可以把這部分補(bǔ)充上了。
對(duì)比昨天的prelogin的URL參數(shù)不能發(fā)現(xiàn),今天的多了兩個(gè)參數(shù):
帶著這兩個(gè)參數(shù)請(qǐng)求服務(wù)器,返回來的也會(huì)多了showpin的值:
既然要顯示pin(驗(yàn)證碼),就要下載驗(yàn)證碼,它的地址是:
https://login.sina.com.cn/cgi/pin.php?r=2855501&s=0&p=aliyun-a34a347956ab8e98d6eb1a99dfddd83bc708
這個(gè)是怎么來的呢?直接按Ctrl F 打開“Text to Find”窗口搜索“pin.php”:
這個(gè)Find窗口很有用,它讓我們可以在記錄的所有請(qǐng)求和響應(yīng)里面查找特定文本,并且它還支持正則表達(dá)式、大小寫敏感、只找全詞。只找全詞,對(duì)查找su這樣的短詞很有幫助,可以過濾大量包含它的詞,比如super。
這里要特別說明一下,為什么只選在”Response Body”里面查找。
因?yàn)槲覀兪且疑厦娴腢RL是如何生成的,我們認(rèn)為它是在某個(gè)js文件的某段代碼實(shí)現(xiàn)的,所以它一定是在 Response Body 里面的,這樣也可以過濾掉很多無關(guān)信息。
通過上面的過濾,直接就定位了相關(guān)代碼,雙擊進(jìn)去,再稍微一搜,就發(fā)現(xiàn)對(duì)應(yīng)的代碼了:
有了這個(gè)js,用Python來實(shí)現(xiàn)就易如反掌了,小猿們可以自己試試看。
有了驗(yàn)證碼的URL,我們就用self.session下載它并保存為文件,在POST 所有l(wèi)ogin數(shù)據(jù)前,通過<code>pin = input('>>please input pin:')</code>來獲取,加入到POST數(shù)據(jù)里面一起POST發(fā)送即可。
第二條可疑的請(qǐng)求的Host跟第一條一樣,路徑是:
POST /sso/login.php?client=ssologin.js(v1.4.19) HTTP/1.1
這是一條POST,我來看看它POST的數(shù)據(jù),選擇這條記錄,點(diǎn)擊“Contents”標(biāo)簽,再點(diǎn)擊“Form”標(biāo)簽,可以看到它POST的數(shù)據(jù):
這時(shí)候我們可以把這寫POST的參數(shù)和prelogin得到的聯(lián)系起來了。
參數(shù):su
這個(gè)看上去是“加密”的username,即用戶名。那它是怎么加密的呢?瀏覽器運(yùn)行的是JavaScript,所以我們猜測(cè)是通過JS加密的,那么是哪段JS呢?看上面login.php路徑里給了參數(shù)client=ssologin.js(v1.4.19),那我們就去ssologin.js里面找找,選擇加載這個(gè)js文件的請(qǐng)求,“Contents”標(biāo)簽下面就會(huì)顯示JS代碼,按Ctrl F查找username:
果然在這里,其實(shí)就是用base64編碼了一下,算不上加密,于是我們就有了獲得su的方法:
**參數(shù):sp**
跟su同樣的思路,還是在ssologin.js里面查找password,我們發(fā)現(xiàn)了加密password的算法:
于是有了獲得sp的方法:
def encrypt_passwd(self, passwd, pubkey, servertime, nonce): key = rsa.PublicKey(int(pubkey, 16), int('10001', 16)) message = str(servertime) '\t' str(nonce) '\n' str(passwd) passwd = rsa.encrypt(message.encode('utf-8'), key) return binascii.b2a_hex(passwd)
參數(shù):prelt
既然ssologin.js就是管登錄的,那我們還是在這里找prelt,Ctrl F 查找到
原理prelt就是preloginTime的簡(jiǎn)稱,那我們?cè)偎阉鱬reloginTime:
preloginTime = (new Date()).getTime() - preloginTimeStart - (parseInt(result.exectime, 10) || 0)
這里的preloginTimeStart就是請(qǐng)求prelogin.php時(shí)的時(shí)間戳,result.exectime就是prelogin請(qǐng)求返回結(jié)果里面的exectime。
哈哈哈,又找到了prelt的算法,其實(shí)這個(gè)prelt就是從請(qǐng)求開始到現(xiàn)在的時(shí)間差,似乎也沒那么重要,隨機(jī)一個(gè)就可以,不過還是用Python實(shí)現(xiàn)一下:
目前,我們已經(jīng)獲得了登錄的重要參數(shù),接下來再看看登錄請(qǐng)求的流程,在“Sequence”的 “Filter” 輸入login,我們可以看到過濾后的請(qǐng)求,其中前三個(gè)就是登錄的先后順序:
其詳細(xì)流程就是:
第4步返回的HTTP頭里面重定向到另外的URL,request會(huì)跟隨這個(gè)重定向,不用我們實(shí)現(xiàn)。
用Python實(shí)現(xiàn)html代碼里面的JS重定向的方法就是,用正則表達(dá)式提取出JS代碼里面的重定向URL,然后用requests做GET請(qǐng)求。
完整的登錄流程的代碼就是:
def login(self): # step-1. prelogin pre_login = self.prelogin() su = self.encrypt_user(self.weibo_user) sp = self.encrypt_passwd( self.weibo_password, pre_login['pubkey'], pre_login['servertime'], pre_login['nonce'] ) prelt = self.get_prelt(pre_login) data = { 'entry': 'weibo', 'gateway': 1, 'from': '', 'savestate': 7, 'qrcode_flag': 'false', 'userticket': 1, 'pagerefer': '', 'vsnf': 1, 'su': su, 'service': 'miniblog', 'servertime': pre_login['servertime'], 'nonce': pre_login['nonce'], 'vsnf': 1, 'pwencode': 'rsa2', 'sp': sp, 'rsakv' : pre_login['rsakv'], 'encoding': 'UTF-8', 'prelt': prelt, 'sr': '1280*800', 'url': 'http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.' 'sinaSSOController.feedBackUrlCallBack', 'returntype': 'META' } # step-2 login POST login_url = 'https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.19)' resp = self.session.post(login_url, data=data) print(resp.headers) print(resp.content) print('Step-2 response:', resp.text) # step-3 follow redirect redirect_url = re.findall(r'location\.replace\('(.*?)'', resp.text)[0] print('Step-3 to redirect:', redirect_url) resp = self.session.get(redirect_url) print('Step-3 response:', resp.text) # step-4 process step-3's response arrURL = re.findall(r''arrURL':(.*?)\}', resp.text)[0] arrURL = json.loads(arrURL) print('CrossDomainUrl:', arrURL) for url in arrURL: print('set CrossDomainUrl:', url) resp_cross = self.session.get(url) print(resp_cross.text) redirect_url = re.findall(r'location\.replace\(\'(.*?)\'', resp.text)[0] print('Step-4 redirect_url:', redirect_url) resp = self.session.get(redirect_url) print(resp.text) with open(self.cookies_tosave, 'wb') as f: pickle.dump(self.session.cookies, f) return True
代碼中打印了很多信息,方便我們過程整個(gè)登錄過程。
要測(cè)試我們的實(shí)現(xiàn)就很簡(jiǎn)單了:
修改為你的微博賬戶和密碼就可以測(cè)試起來啦。
聯(lián)系客服