概述:
由于業務需要,要編寫爬蟲代碼去爬去新浪微博用戶的信息。
雖然在網上能找到不少信息,但由于新浪微博改版,其登陸機制進行了修改,故很多老的文章就不適合用了。
經過一番摸索,成功模擬新浪微博的登陸操作,項目使用的是Javascript語言,在ChromeExtension中運行, 跟大家分享一下這一過程和心得。
注意,本文寫于2013.08.16,由于微博業務經常變化,隨年月老去,文章可能不再適用。
正文:探索的過程
1. 了解大致過程
登陸微博,使用fiddler工具監視HTTP請求,截獲如下操作:
可以看到在微博登陸的整個過程中,有四次重要的HTTP請求,分別是:
(1) GET /sso/prelogin.php
(2) POST /sso/login.php
(3) GET /ajaxlogin.php
(4) GET /u/2813262187
其中,sso是single sign on
(1) 是登陸前的預處理,名字也顯而易見--pre,這一步操作主要是拿到了幾個重要的參數,用作下一步POST表單的參數。
可以在瀏覽器中訪問這一請求的地址,查看返回的結果:
結果如下:
2 "servertime":1376533839,
3 "pcid":"gz-7bdd82b8980057a8bbc1f86b21d5a86184dd",
4 "nonce":"R1KGHZ",
5 "pubkey":"EB2A38568661887FA180BDDB5CABD5F21C7BFD59C090CB2D245A87AC253062882729293E5506350508E7F9AA3BB77F4333231490F915F6D63C55FE2F08A49B353F444AD3993CACC02DB784ABBB8E42A9B1BBFFFB38BE18D78E87A0E41B9B8F73A928EE0CCEE1F6739884B9777E4FE9E88A1BBE495927AC4A799B3181D6442443",
6 "rsakv":"1330428213",
7 "exectime":2})
返回的數據有servertime:服務時間戳,nonce:是一個6位隨機碼,pubkey:是用于rsa2密碼加密的公鑰,rsakv:顯然也是加密用的。
這四個值都是需要在下一步用到的。
在久版本的新浪微博登陸機制當中,使用的是sha的加密方式,沒有pubkey和rsakv參數,
因此,在網上看到的2012年的爬蟲代碼模擬登陸的代碼都已經不適合用,但總體邏輯是沒多大變化的,只需要稍作修改還是能用的,我就是這么走過來的,碰壁無數=.=
給出兩篇參考文章:
For Python http://www.douban.com/note/201767245/
For PHP http://www.2cto.com/kf/201210/159591.html
(2) 提交表單的操作,我們依然是查看HTTP請求,看表單內容
看到了,servertime、nonce、rsakv,還有su:加密后的用戶名,sp:加密后的密碼。現在問題就是用戶名跟密碼是怎么加密的了,很容易地我們的目光聚焦到js代碼上面
請求新浪登陸頁面:http://login.sina.com.cn/signup/signin.php,里面有用于密碼加密的js代碼http://login.sina.com.cn/js/sso/ssologin.js,
里面的代碼是加密過的,找個工具解密一下(http://js.clicki.cn/), 勉強還能看到其中的邏輯的。
(3) 分析ssologin.js
既然我們知道了servertime跟nonce是用于加密的,不妨在文件中搜一下這兩個關鍵字,一搜找到了好東西:
翻回去上文看看,表單中有個su和sp,都在這里了,可以看到su使用的是base64加密方式;而su就稍微復雜一點了,
if中的是新浪當前版本的密碼加密方式rsa2的代碼,而else中的是就版本sha加密的代碼,我們只需要關心if中的內容,
加密過程很簡單,先是生成一個RSAKey對象, set PublicKey,然后就加密了。
一項一項地倒推回去,可以知道最后一步加密中最后的一個參數ag是原始的密碼值,不會看到眼花的~
概括一下,就是用好長的那一串pubkey和10001作參數調用setPublic() ,注意的是它們都十六進制數;然后用(servertime + nonce + pwd)作參數調用Encrypt()。
我們去看看sinaSSOEncoder是個什么東西,它的定義在解密了的代碼的1118行,從這開始截取到文件末尾,挪出來我們自己用,
2 //sinaSSOController = new SSOController();
3 //sinaSSOController.init();
這就解決了密碼加密的問題了。
2. 模擬請求過程
既然知道大致的原理,就可以開始模擬整個登錄的過程了,
(1) GET請求 http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=&rsakt=mod&client=ssologin.js(v1.4.11)
返回的數據中正則匹配到servertime、nonce和rsakv,注意,在請求的時候要把時間戳也一并傳到服務器。我們看看fiddler監控到的QueryString:
前五個參數都在URL里面顯示聲明了,最后的_就自己在發請求的時候補上,傳過去吧。
我使用的是Javascript,用Jquery,$.ajax中的data屬性里面加進去就好了。
(2) POST請求 http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.11) 提交登錄表單
2 {type: 'POST', headers: { Referer : 'http://weibo.com/?from=bp&lang=zh-cn'},
3 data : {
4 entry:'weibo',
5 gateway:1,
6 from:'',
7 savestate:7,
8 useticket:1,
9 pagerefer:'http://weibo.com/a/download',
10 vsnf:1,
11 su:'MTgxMDU0MjMzNyU0MHFxLmNvbQ==',
12 service:'miniblog',
13 servertime: servertime,
14 nonce: nonce,
15 pwencode:'rsa2',
16 rsakv: rsakv,
17 sp: sp,
18 encoding:'UTF-8',
19 prelt:505,
20 url:'http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack',
21 returntype:'META'
22 }
23 }, function(err, data){ ... });
agent是發送請求事務的封裝模塊,重點看里面的data對象,其中su是base64加密的用戶名,每次調用都是固定的,因此寫死在這里;
servertime、nonce、rsakv都是在前一步截取的;
sp是加密后的密碼,參考上文的加密方式可得。
(代碼會在后面分享出來)
(3) 上一步頁面請求完成后,返回的數據如下所示
這段代碼執行后,頁面直接跳轉到新的地址上,雖然我們上微博的時候感覺不到有中間頁面的跳轉,
其中retcode是從服務器返回過來的,為0時才表示正確,能繼續訪問,如果返回的是其它值,可以看看ssologin.js的源碼,里面有各種錯誤的說明,
本人也在這個地方糾結了很久,之前嘗試都是2092(好像是),后來把前面兩步的參數傳完整(如_),補上Referer,就好了。所以要盡量模擬完整。
第三步就是要截取這個URL,請求之。至此,登錄操作完成。
(4) GET請求 http://weibo.com/u/2813262187?wvr=5&wvr=5&lf=reg ,這是個人主頁,能返回個人信息則表示前面的操作成功了,如果是返回登錄頁,那不好意思,前面的步驟還是再檢查一下吧~
參考文章:
老版本新浪,Python http://www.douban.com/note/201767245/
老版本新浪,PHP http://www.2cto.com/kf/201210/159591.html
新版本新浪,Python http://www.cnblogs.com/mouse-coder/archive/2013/03/03/2941265.html
JQuery Ajax API http://api.jquery.com/jQuery.ajax/
ChromeExtension https://developer.chrome.com/extensions/webRequest.html#event-onHeadersReceived
附:代碼 http://files.cnblogs.com/sysuys/weiboLogin.rar
說明:
1. 這幾個文件是從爬蟲項目抽取出來的,所以這些文件跑不起來,但可供參考,代碼沒有完善,沒注釋
2. 項目是運行在Chrome插件中的
3. 使用了requirejs.js
文章列表