python 自動登陸網頁原理
- 2020 年 1 月 8 日
- 筆記
有些網站設置了許可權,只有在登錄了之後才能爬取網站的內容,如何模擬登錄,目前的方法主要是利用瀏覽器cookie模擬登錄。
瀏覽器訪問伺服器的過程
在用戶訪問網頁時,不論是通過URL輸入域名或IP,還是點擊鏈接,瀏覽器向WEB伺服器發出了一個HTTP請求(Http Request),WEB伺服器接收到客戶端瀏覽器的請求之後,響應客戶端的請求,發回相應的響應資訊(Http Response),瀏覽器解析引擎,排版引擎分析返回的內容,呈現給用戶。WEB應用程式在於伺服器交互的過程中,HTTP請求和響應時發送的都是一個消息結構。

Http消息
當瀏覽器向伺服器發送請求的時候,發出http請求消息報文,伺服器返回數據時,發出http響應消息報文,這兩種類型的消息都是由一個起始行,消息頭,一個指示消息頭結束的空行和可選的消息體組成。http請求消息中,起始行包括請求方法,請求的資源, HTTP協議的版本號,消息頭包含各種屬性,消息體包含數據,GET請求並沒有消息主體,因此在消息頭後的空白行中沒有其他數據。Http響應消息中,起始行包括HTTP協議版本,http狀態碼和狀態,消息頭包含各種屬性,消息體包含伺服器返回的數據內容。

如下圖從fiddler抓取的http請求和http響應,GET請求內容為空,故消息頭之後的空行和消息體都為空。

伺服器發送的響應消息如下,瀏覽器正常接收到伺服器發回的http報文


從上可以看到,cookie在http請求和http響應的頭資訊中,cookie是消息頭的一種很重要的屬性。
什麼是Cookie?
當用戶通過瀏覽器首次訪問一個域名時,訪問的WEB伺服器會給客戶端發送數據,以保持WEB伺服器與客戶端之間的狀態保持,這些數據就是Cookie,它是 Internet 站點創建的 ,為了辨別用戶身份而儲存在用戶本地終端上的數據,Cookie中的資訊一般都是經過加密的,Cookie存在快取中或者硬碟中,在硬碟中的是一些小文本文件,當你訪問該網站時,就會讀取對應網站的Cookie資訊,Cookie有效地提升了我們的上網體驗。一般而言,一旦將 Cookie 保存在電腦上,則只有創建該 Cookie 的網站才能讀取它。

為什麼需要Cookie
Http協議是一個無狀態的面向連接的協議,Http協議是基於tcp/ip協議層之上的協議,當客戶端與伺服器建立連接之後,它們之間的TCP連接一直都是保持的,至於保持的時間是多久,是通過伺服器端來設置的,當客戶端再一次訪問該伺服器時,會繼續使用上一次建立的連接,但是,由於Http協議是無狀態的,WEB伺服器並不知道這兩個請求是否同一個客戶端,這兩次請求之間是獨立的。 為了解決這個問題, Web程式引入了Cookie機制來維護狀態.cookie可以記錄用戶的登錄狀態,通常web伺服器會在用戶登錄成功後下發一個簽名來標記session的有效性,這樣免去了用戶多次認證和登錄網站。記錄用戶的訪問狀態。
Cookie的種類
會話Cookie(Session Cookie):這個類型的cookie只在會話期間內有效,保存在瀏覽器的快取之中,用戶訪問網站時,會話Cookie被創建,當關閉瀏覽器的時候,它會被瀏覽器刪除。 持久Cookie(Persistent Cookie): 這個類型的cookie長期在用戶會話中生效。當你設置cookie的屬性Max-Age為1個月的話,那麼在這個月里每個相關URL的http請求中都會帶有這個cookie。所以它可以記錄很多用戶初始化或自定義化的資訊,比如什麼時候第一次登錄及弱登錄態等。 Secure cookie:安全cookie是在https訪問下的cookie形態,以確保cookie在從客戶端傳遞到Server的過程中始終加密的。 HttpOnly Cookie :這個類型的cookie只能在http(https)請求上傳遞,對客戶端腳本語言無效,從而有效避免了跨站***。 第三方cookie: 第一方cookie是當前訪問的域名或子域名下的生成的Cookie。 第三方cookie:第三方cookie是第三方域名創建的Cookie。
Cookie的構成
Cookie是http消息頭中的一種屬性,包括:Cookie名字(Name)Cookie的值(Value),Cookie的過期時間(Expires / Max-Age),Cookie作用路徑(Path),Cookie所在域名(Domain),使用Cookie進行安全連接(Secure)。 前兩個參數是Cookie應用的必要條件,另外,還包括Cookie大小(Size,不同瀏覽器對Cookie個數及大小限制是有差異的)。
python模擬登錄
設置一個cookie處理對象,它負責 將cookie添加到http請求中,並能從http響應中得到cookie , 向網站登錄頁面發送一個請求Request, 包括登錄url,POST請求的數據,Http header 利用urllib2.urlopen發送請求,接收WEB伺服器的Response。 首先我們查看登陸頁面源碼

當我們使用urllib處理url的時候,實際上是通過urllib2.OpenerDirector實例進行工作,他會自己調用資源進行各種操作如通過協議、打開url、處理cookie等。而urlopen方法使用的是默認的opener來處理問題,基本的urlopen()函數不支援驗證、cookie或其他的HTTP高級功能。要支援這些功能,必須使用build_opener()函數來創建自己的自定義Opener對象。 cookielib模組定義了自動處理HTTP cookies的類,用來訪問那些需要cookie數據的網站,cookielib模組包括CookieJar,FileCookieJar,CookiePolicy,DefaultCookiePolicy,Cookie及FileCookieJar的子類MozillaCookieJar和LWPCookieJar,CookieJar對象可以管理HTTP cookies,將cookie添加到http請求中,並能從http響應中得到cookie,FileCookieJar對象主要是從文件中讀取cookie或創建cookie,其中,MozillaCookieJar是為了創建與Mozilla瀏覽器cookies.txt兼容的FileCookieJar實例,LWPCookieJar是為了創建與libwww-perl的Set-Cookie3文件格式兼容的FileCookieJar實例,用LWPCookieJar保存的cookie文件易於人類閱讀。默認的是FileCookieJar沒有save函數,而MozillaCookieJar或LWPCookieJar都已經實現了。 所以可以用MozillaCookieJar或LWPCookieJar,去自動實現cookie的save。
示例:
#! /usr/bin/env python #coding:utf-8 import sys import re import urllib2 import urllib import requests import cookielib ## 這段程式碼是用於解決中文報錯的問題 reload(sys) sys.setdefaultencoding("utf8") ##################################################### #登錄人人 loginurl = 'http://www.renren.com/PLogin.do' logindomain = 'renren.com' class Login(object): def __init__(self): self.name = '' self.passwprd = '' self.domain = '' self.cj = cookielib.LWPCookieJar() self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cj)) urllib2.install_opener(self.opener) def setLoginInfo(self,username,password,domain): '''設置用戶登錄資訊''' self.name = username self.pwd = password self.domain = domain def login(self): '''登錄網站''' loginparams = {'domain':self.domain,'email':self.name, 'password':self.pwd} headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36'} req = urllib2.Request(loginurl, urllib.urlencode(loginparams),headers=headers) response = urllib2.urlopen(req) self.operate = self.opener.open(req) thePage = response.read() if __name__ == '__main__': userlogin = Login() username = 'username' password = 'password' domain = logindomain userlogin.setLoginInfo(username,password,domain) userlogin.login()