我決定從頭說起捧灰。懂的人可以快速略過前面理論看最后幾張圖。
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)一資源定位符)來鏈接至服務器。
通常一個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掸读。
- Domain:指示cookie來自什么地方串远,即域名宏多。每個域至多在客戶端存儲20個cookie。
- Path:服務器目錄結構中的一個路徑抑淫,表示服務器文件樹的哪些部分可能會用到該cookie绷落。通常是/姥闪,表示整個文件樹始苇。
- Content:采用"name = value"的形式,即鍵值對筐喳,熟悉python的話可以得知Dictionary使用相同結構催式。這是cookie內(nèi)容存放之處。
- Expires:過期時間避归。如果此域不存在的話荣月,瀏覽器在退出時會將cookie丟棄。否則會一直在客戶端保存到過期為止梳毙。所以如果服務器想將客戶端的cookie刪除哺窄,只需要再將它發(fā)送一次,并且選擇一個已經(jīng)過去的時間作為Expires账锹。
- 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ā)生在客戶端漱抓。
服務器端動態(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)仲器。客戶端動態(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绿淋。
GET方法請求最常用闷畸,直接以鏈接形式訪問,即鏈接中包含了所有參數(shù)吞滞。速度快佑菩,內(nèi)容量小,不安全冯吓。
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的頭域包括通用頭巷嚣,請求頭,響應頭和實體頭四個部分钳吟。每個頭域由一個域名廷粒,冒號(:)和域值三部分組成。
- 通用頭域(即通用頭)
通用頭域包含請求和響應消息都支持的頭域红且。通用頭域包含Cache-Control坝茎、 Connection、Date直焙、Pragma景东、Transfer-Encoding、Upgrade奔誓、Via斤吐。 - 請求消息(請求頭)
請求消息的第一行為下面的格式:
Method Request-URI HTTP-Version
Host頭域指定請求資源的Intenet主機和端口號,必須表示請求url的原始服務器或網(wǎng)關的位置厨喂。HTTP/1.1請求必須包含主機頭域和措,否則系統(tǒng)會以400狀態(tài)碼返回。
Accept:告訴WEB服務器自己接受什么介質(zhì)類型蜕煌,/ 表示任何類型派阱,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ā)送趾断。 - 響應消息(響應頭)
響應消息的第一行為下面的格式:
HTTP-Version Status-Code Reason-Phrase
HTTP -Version表示支持的HTTP版本拒名,例如為HTTP/1.1。
Status- Code是一個三個數(shù)字的結果代碼芋酌。
Reason-Phrase給Status-Code提供一個簡單的文本描述增显。 - 實體消息(實體頭和實體)
請求消息和響應消息都可以包含實體信息,實體信息一般由實體頭域和實體組成隔嫡。
整理思路甸怕,開始動手
- 我的目的:到人人網(wǎng)將我的各種信息都download到本地。
- 遇到問題:人人網(wǎng)需要用戶登錄腮恩。
- 解決辦法:只需要獲得服務器返回的cookie,以后每次訪問頁面時將此cookie一并發(fā)送至服務器就可以了温兼。
- 遇到問題:我不會手工構建cookie秸滴,怎么獲取。
- 解決辦法:先手工模擬瀏覽器登錄人人網(wǎng)募判,獲取第一個cookie荡含。
- 遇到問題:怎么模擬登錄。
- 解決辦法:向登錄頁面發(fā)送http登錄請求届垫。
- 遇到問題:登錄頁面的具體URL是什么释液;http登錄請求需要包含哪些信息。
解決這兩個問題后装处,這篇博客就圓滿了误债。
登錄URL
需要進行抓包,我選用Chrome妄迁,因為我的Mac上只有這個寝蹈。
其實在抓包之前我先看了下網(wǎng)頁的編碼,如圖登淘,打開chrome的開發(fā)者工具(option+command+i)箫老,點擊Elements,然后點擊左上方的放大鏡圖標黔州,再去網(wǎng)頁頁面上點擊“登錄”按鈕耍鬓,發(fā)現(xiàn)了圖中的結構體阔籽。
我選中的那一行已經(jīng)明確表示了,表單方法是“POST”牲蜀,猜測action屬性就是提交表單的對象笆制,也就是登錄URL。然而到底是不是呢各薇?這只能算個猜測项贺,具體是什么URL,用抓包去得到的結果比較有說服力峭判。
點擊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下是默認勾選的衙传。
找找為數(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ù)犀填,盡量還原蠢壹,但是夠用就好。
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罷了。在下面的第二章截圖中渣玲,兩種方式均有實例逗概。
這里只是簡單的構造一個Request⊥埽可以看到我試用了兩個loginURL逾苫,兩者最后都返回了cookie卿城。
并且我分別嘗試用這兩個cookie去打開http://www.renren.com ,發(fā)現(xiàn)都成功進入了我的賬戶铅搓!
最后提出一個問題:登錄URL的時間戳是咋回事瑟押?
目前測試前天的還可以用。分析一下星掰,首先這個URL是自動生成的多望,然后和登錄相關,猜測是在前端編寫的氢烘。
很快就找到了疑似的js腳本怀偷。
打開,搜“uniqueTimestam”威始。
像我一樣不懂JS的人可以百度下“js Date”枢纠,查看函數(shù)說明。按照解釋黎棠,我截圖中“201582021961”的意思依次是:2015年晋渺,9月,星期三脓斩,0點木西,21秒,961毫秒(居然沒有MinutesK婢病)
也可以直接在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六剥,此時登錄成功。
另外峰伙,為了方便疗疟,將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著