用python實現(xiàn)模擬登錄人人網(wǎng)

我決定從頭說起。懂的人可以快速略過前面理論看最后幾張圖。

web基礎知識

從OSI參考模型(從低到高:物理層,數(shù)據(jù)鏈路層,網(wǎng)絡層,傳輸層,會話層,表示層,應用層)來說,我們的互聯(lián)網(wǎng)屬于應用層。從TCP/IP參考模型(從低到高:物理層,數(shù)據(jù)鏈路層,網(wǎng)絡層,傳輸層,應用層)來說,也同樣如此。

互聯(lián)網(wǎng)上有各種各樣的資源,包括文本、圖片、音頻、視頻……

通常所見的Web模型需要包括兩部分:客戶端,服務器。個人電腦上的瀏覽器就是一種客戶端,而保存我們輸入的網(wǎng)址所有相關資源的就是服務器。

客戶端通過URL(Uniform Resource Locator,統(tǒng)一資源定位符)來鏈接至服務器。

http://www.abcd.com/page.html

通常一個URL如上所示,http是協(xié)議名(http、ftp、telnet……),www.abcd.com是頁面所在機器的DNS名,page.html是頁面文件的名字。

內(nèi)部細節(jié)不表,總之我們在瀏覽器中輸入url,瀏覽器通過url與服務器經(jīng)過復雜溝通協(xié)調(diào)建立聯(lián)系(TCP、UDP……),將數(shù)據(jù)從服務器發(fā)至瀏覽器,我們能夠通過瀏覽器看到最終拿到的資源。

通常我們看到的web頁面,是使用HTML的語言來編寫的。上面除了有之前提到的各種資源外,還有超鏈接。

Cookie

新的需求出現(xiàn)了:服務器需要能夠認識客戶端,比如客戶需要登錄、需要身份識別、需要權限識別、需要依據(jù)不同客戶加載不同內(nèi)容……

怎么辦?當客戶端向服務器端請求一個web頁面時,服務器端除了提供此頁面外,還會提供一些附加信息,其中包括cookie,客戶端會將cookie存儲在客戶機器的本地磁盤上。

當瀏覽器向該服務器端發(fā)起請求時,瀏覽器會檢查它的cookie,確定所請求的目標域是否有存cookie,如果有,會將該cookie包含到請求消息(request)中。服務器得到cookie后,就可以進行識別操作了。

一個cookie可以保函至多5個域:Domain、Path、Content、Expires、Secure。

  1. Domain:指示cookie來自什么地方,即域名。每個域至多在客戶端存儲20個cookie。
  2. Path:服務器目錄結構中的一個路徑,表示服務器文件樹的哪些部分可能會用到該cookie。通常是/,表示整個文件樹。
  3. Content:采用"name = value"的形式,即鍵值對,熟悉python的話可以得知Dictionary使用相同結構。這是cookie內(nèi)容存放之處。
  4. Expires:過期時間。如果此域不存在的話,瀏覽器在退出時會將cookie丟棄。否則會一直在客戶端保存到過期為止。所以如果服務器想將客戶端的cookie刪除,只需要再將它發(fā)送一次,并且選擇一個已經(jīng)過去的時間作為Expires。
  5. Secure:指示瀏覽器只向安全的服務器(https等等)才返回該cookie。

到這里,我們知道了,后續(xù)每次連接人人網(wǎng)的時候,都需要一個正確的cookie來表明我們的身份。

靜態(tài)和動態(tài)Web文檔

按照之前提到的模型,客戶端向服務器端發(fā)出Request,服務器端返回Response,一次完成,不管傳輸?shù)氖荋TML格式、還是XML格式、還是……這種都算是靜態(tài)Web文檔,過程非常簡單。

然而,社會在進步,需求在增加。越來越多的內(nèi)容需要根據(jù)實際情況來生產(chǎn),并且內(nèi)容的生成既可以發(fā)生在服務器端,也可發(fā)生在客戶端。

  1. 服務器端動態(tài)Web頁面
    用戶可以在客戶端提交表單(Form)給服務器端,服務器端需要根據(jù)表單內(nèi)容來返回正確的Web頁面。
    有兩種處理表單或類似Request的方法。
    傳統(tǒng)方法是CGI系統(tǒng),允許服務器與后端程序及腳本通信,通常用Perl或Python編寫。簡單的模型是:用戶,瀏覽器,服務器,CGI腳本,磁盤上的數(shù)據(jù)庫,以上幾個呈鏈型。
    另外一種方法是在HTML頁面中嵌入少量腳本,讓服務器來執(zhí)行這些腳本以便生成最終發(fā)送給客戶的頁面。通常用PHP、JSP、ASP等。簡單的模型是:用戶,瀏覽器,服務器,而PHP則作為一個模塊存在于服務器內(nèi)。

  2. 客戶端動態(tài)Web頁面
    然而CGI、PHP、JSP、ASP這種在部署在服務器端的腳本并不能夠對用戶的鼠標移動事件進行響應,或者直接與用戶交互。
    現(xiàn)在經(jīng)常會談到“互聯(lián)網(wǎng)服務,交互界面要友好”,想要達成這一目的,需要在客戶端能夠實時依據(jù)用戶的動作來處理,現(xiàn)在最流行的應該是Javascript了吧。簡單的模型依舊是:用戶,瀏覽器,服務器,只不過在瀏覽器多了一個Javascript的模塊。

這兩種動態(tài)web語言并沒有誰優(yōu)誰劣的區(qū)分,只不過用途不同罷了。前者就是我們常說的后端,后者就是我們常說的前端。

HTTP——超文本傳輸協(xié)議

前面提到,URL的第一個部分是協(xié)議名,我們用的比較多的是http協(xié)議。

Request

客戶端到服務器的消息被稱作請求(Request),需要包括應用于資源的方法、資源的標識符和協(xié)議的版本號。

HTTP的Request包括幾種訪問方法:GET/HEAD/PUT/POST/DELETE/TRACE/CONNECT/OPTIONS。

  1. GET方法請求最常用,直接以鏈接形式訪問,即鏈接中包含了所有參數(shù)。速度快,內(nèi)容量小,不安全。

  2. POST方法經(jīng)常和GET進行比較,它是向服務器發(fā)送請求,要求服務器接受位于請求后的實體,依據(jù)實體內(nèi)容處理后返回相關內(nèi)容。速度慢,內(nèi)容量大,安全。

However,不再以訛傳訛,GET和POST的真正區(qū)別在這篇博客中,作者查證HTTP協(xié)議后說:

在HTTP協(xié)議中,Method和Data(URL, Body, Header)是正交的兩個概念,也就是說,使用哪個Method與應用層的數(shù)據(jù)如何傳輸是沒有相互關系的。

HTTP沒有要求,如果Method是POST數(shù)據(jù)就要放在BODY中。也沒有要求,如果Method是GET,數(shù)據(jù)(參數(shù))就一定要放在URL中而不能放在BODY中。

那么,網(wǎng)上流傳甚廣的這個說法是從何而來的呢?我在HTML標準中,找到了相似的描述。這和網(wǎng)上流傳的說法一致。但是這只是HTML標準對HTTP協(xié)議的用法的約定。怎么能當成GET和POST的區(qū)別呢?

之后,該博客還對其他幾個區(qū)別點進行了駁斥。所以,上面所說的是按照HTML標準,按照HTTP標準的話,GET方法請求服務器發(fā)送頁面,POST方法則是要寫入頁面(所以POST方法通常要有Content-Type和認證頭,通過認證頭來證明有權執(zhí)行所請求的操作)。

Response

從服務器返回的消息稱作響應(Response),包括HTTP協(xié)議的版本號、請求的狀態(tài)(成功與否,等等)和文檔的MIME類型。

狀態(tài)行包括一個3位數(shù)字的狀態(tài)碼,如2xx表示成功,常見200表示請求成功;3xx表示進行了重定向;4xx表示客戶錯誤,常見403是禁止的頁面,404是頁面未找到;5xx是服務器錯誤。

消息頭

HTTP的頭域包括通用頭,請求頭,響應頭和實體頭四個部分。每個頭域由一個域名,冒號(:)和域值三部分組成。

  1. 通用頭域(即通用頭)
    通用頭域包含請求和響應消息都支持的頭域。通用頭域包含Cache-Control、 Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via。
  2. 請求消息(請求頭)
    請求消息的第一行為下面的格式:
    Method Request-URI HTTP-Version
    Host頭域指定請求資源的Intenet主機和端口號,必須表示請求url的原始服務器或網(wǎng)關的位置。HTTP/1.1請求必須包含主機頭域,否則系統(tǒng)會以400狀態(tài)碼返回。
    Accept:告訴WEB服務器自己接受什么介質類型,/ 表示任何類型,type/* 表示該類型下的所有子類型,type/sub-type。
    Accept-Charset: 瀏覽器申明自己接收的字符集。
    Authorization:當客戶端接收到來自WEB服務器的 WWW-Authenticate 響應時,用該頭部來回應自己的身份驗證信息給WEB服務器。
    User-Agent頭域的內(nèi)容包含發(fā)出請求的用戶信息。
    Referer 頭域允許客戶端指定請求uri的源資源地址,這可以允許服務器生成回退鏈表,可用來登陸、優(yōu)化cache等。如果請求的uri沒有自己的uri地址,Referer不能被發(fā)送。
  3. 響應消息(響應頭)
    響應消息的第一行為下面的格式:
    HTTP-Version Status-Code Reason-Phrase
    HTTP -Version表示支持的HTTP版本,例如為HTTP/1.1。
    Status- Code是一個三個數(shù)字的結果代碼。
    Reason-Phrase給Status-Code提供一個簡單的文本描述。
  4. 實體消息(實體頭和實體)
    請求消息和響應消息都可以包含實體信息,實體信息一般由實體頭域和實體組成。

整理思路,開始動手

  1. 我的目的:到人人網(wǎng)將我的各種信息都download到本地。
  2. 遇到問題:人人網(wǎng)需要用戶登錄。
  3. 解決辦法:只需要獲得服務器返回的cookie,以后每次訪問頁面時將此cookie一并發(fā)送至服務器就可以了。
  4. 遇到問題:我不會手工構建cookie,怎么獲取。
  5. 解決辦法:先手工模擬瀏覽器登錄人人網(wǎng),獲取第一個cookie。
  6. 遇到問題:怎么模擬登錄。
  7. 解決辦法:向登錄頁面發(fā)送http登錄請求。
  8. 遇到問題:登錄頁面的具體URL是什么;http登錄請求需要包含哪些信息。

解決這兩個問題后,這篇博客就圓滿了。

登錄URL

需要進行抓包,我選用Chrome,因為我的Mac上只有這個。

其實在抓包之前我先看了下網(wǎng)頁的編碼,如圖,打開chrome的開發(fā)者工具(option+command+i),點擊Elements,然后點擊左上方的放大鏡圖標,再去網(wǎng)頁頁面上點擊“登錄”按鈕,發(fā)現(xiàn)了圖中的結構體。

我選中的那一行已經(jīng)明確表示了,表單方法是“POST”,猜測action屬性就是提交表單的對象,也就是登錄URL。然而到底是不是呢?這只能算個猜測,具體是什么URL,用抓包去得到的結果比較有說服力。

登錄地址猜測.png

點擊Network,將左上方的圓圈點擊變成紅色,這樣chrome就會將打開網(wǎng)頁的過程抓取下來。

然而這么做我并沒有找到登錄時的POST Request,我想了好久找了好多參考資料都沒寫是咋回事。但是當我使用win7下的chrome,以及IE時,均找到了對應的Request。

這是咋回事?經(jīng)過兩者的對比,我發(fā)現(xiàn)在Mac下,chrome抓包結果少了很多,尤其是當我在登錄界面輸入用戶名,并切換焦點時,會生成一個“ShowCaptcha”的Post Request(圖中有),結果點擊“登錄”后就消失不見了。我猜測是由于在登錄的過程中頁面進行了跳轉,使得之前抓取的信息都被覆蓋掉了。仔細尋找發(fā)現(xiàn)了“Preserve log”,鼠標停在按鈕上的解釋是“Do not clear log on page reload/navigation”,選取后才一切正常了,結果如圖。

個人判斷,此按鈕在mac下才在界面上顯示,win下是默認勾選的。

屏幕快照 2015-09-21 下午11.48.43.png

找找為數(shù)不多的幾個POST Request,不難發(fā)現(xiàn)就是圖中的倒數(shù)第三個。點擊可以看到它的頭部信息。

獲得信息,Request URL:http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=20158113687

url最后寫uniqueTimestamp,說明它是有時間戳的,過段時間此數(shù)值就不一樣了。

在該界面的最下方可以找到Post Request的Form Data,由于可能泄露隱私,就不截圖了。觀察可發(fā)現(xiàn)其中有兩個關鍵參數(shù):email,是我的登錄郵箱;password,是經(jīng)過處理后的密碼。還有其他一些參數(shù),盡量還原,但是夠用就好。

屏幕快照 2015-09-22 上午12.04.22.png

http登錄請求包

實際上如何構造http登錄請求包是個復雜的過程,若登錄的是taobao這類安全要求較高的網(wǎng)站,需要構造很多校驗內(nèi)容。人人網(wǎng)倒是非常簡單,直接POST Request即可,并且發(fā)現(xiàn)人人并沒有校驗HTTP頭部。

這也算是最簡單的一種了吧。

并且請求包很靈活,每個網(wǎng)站的要求還不太一樣,所以如果以后需要解析其他請求包時,再單獨分析吧。希望不要太復雜。

首先聲明一個cookieJar的對象來保存cookie,然后使用urllib2的HTTPCookieProcessor來創(chuàng)建cookie處理器,通過此處理器來構建opener,并將此opener設置為urllib2的默認opener。

從此之后,只要使用此opener,就可以使用當前的cookie了。也可以直接使用self.opener,效果是一樣的,只不過這里是顯式調(diào)用,之前的install之后使用urlllib2看不到具體的opener罷了。在下面的第二章截圖中,兩種方式均有實例。

屏幕快照 2015-09-22 上午12.55.44.png

這里只是簡單的構造一個Request。可以看到我試用了兩個loginURL,兩者最后都返回了cookie。

并且我分別嘗試用這兩個cookie去打開http://www.renren.com ,發(fā)現(xiàn)都成功進入了我的賬戶!

屏幕快照 2015-09-22 上午1.23.29.png

最后提出一個問題:登錄URL的時間戳是咋回事?

目前測試前天的還可以用。分析一下,首先這個URL是自動生成的,然后和登錄相關,猜測是在前端編寫的。

很快就找到了疑似的js腳本。

屏幕快照 2015-09-23 上午12.03.30.png

打開,搜“uniqueTimestam”。

屏幕快照 2015-09-23 上午12.04.36.png

像我一樣不懂JS的人可以百度下“js Date”,查看函數(shù)說明。按照解釋,我截圖中“201582021961”的意思依次是:2015年,9月,星期三,0點,21秒,961毫秒(居然沒有Minutes?。?/p>

也可以直接在Chrome的開發(fā)者工具里面點擊“Console”,直接輸入截圖中的命令就可以獲得當前的取值。

同時,我們從這段js代碼中,又一次確認了登錄URL,整個登錄也的確是一個簡單的Request。其中的變量c存儲了POST方法要發(fā)送的data(我猜的,畢竟我不懂js)。但是怎么判斷這個時間戳是有效的我沒看出來,我把代碼中的時間戳改成2014年也依然有效??傊绻蟮卿浭×?,我們可以選擇重新生成一個新的時間戳。

另附帶幾個chrome開發(fā)工具使用說明:

Network界面左上方的禁止圖標,點擊后可以清楚當前抓取的所有信息。

有時需要清除瀏覽器的Cache和Cookie,此時在Network右鍵即可看到選項。


2016年3月28日更新:

人人網(wǎng)登錄時需要進行輸入驗證碼,之前的方法不能用了。
思路是向服務器獲取驗證碼,進行人工識別,然后將包含驗證碼的data去進行登錄操作,成功后即可得到cookie。
參考:人人網(wǎng)登錄腳本(含登錄失敗及驗證碼處理)
成功的標志是得到的cookie中有一項的name為id,它所對應的value即為登錄賬號的userID,此時登錄成功。

屏幕快照 2016-04-01 下午9.32.55.png

另外,為了方便,將cookie保存到本地,若此cookie無法使用,再使用上述的方法獲取新cookie。
首先在初始化時,使用:
cookielib.LWPCookieJar().load(filename,ignore_discard=True, ignore_expires=True)
將filename中的cookie鍵值對導入變量中。
ignore_discard的意思是即使cookies將被丟棄也將它保存下來,ignore_expires的意思是如果在該文件中cookies已經(jīng)存在,則覆蓋原文件寫入。
如果登錄失敗,則得到的url中有failCode,根據(jù)人人網(wǎng)的規(guī)則,等于512即為驗證碼未通過。

獲取成功后,需要將cookie保存到該文件中:
cookielib.LWPCookieJar().save(filename,ignore_discard=True, ignore_expires=True)

有一點需要注意的是,獲取驗證碼圖片的url為:
http://icode.renren.com/getcode.do?t=login&rnd=Math.random()
其中t不可以為web_login,若設置為此值,則意味著在之后的提交時相當于頁面刷新了一次,傳送過去的驗證碼和當前刷新過后的驗證碼已不相等,導致登錄失敗。


最后的總代碼:RenRenDownload

參考書目

《計算機網(wǎng)絡(第4版)》 Andrew S. Tanenbaum著

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容