在用瀏覽器進行網頁訪問時,會向網頁所在的服務器發送http協議的GET或者POST等請求,在請求中除了指定所請求的方法以及URI以外,後面還跟隨着一段Request Header。Request Header的內容主要用於描述本地信息,如所用的瀏覽器、所用的系統、語言、所能接受的返回數據的編碼格式等,其中有一個很是重要的Header項就是Cookie,Cookie能夠說是網站的自定義數據集。因爲服務器端沒法沒法控制本地(瀏覽器)的內存數據,但服務器又有必要蒐集與本身所提供的服務相關的本地狀態信息,而Cookie就承載了這一功能,目的是記錄用戶在網站的狀態信息。html
在用python對網頁進行訪問的時候,若是但願獲得與在網頁端相同的結果,用該網頁在瀏覽器中所保留的Cookie做爲python的請求Cookie是一個比較值得推薦的作法。python
本文主要討論如何提取瀏覽器保存在本地的Cookie,所用的瀏覽器爲Chrome。git
Chrome用sqlite來維護Cookie,Cookie中的信息被保存在sqlite數據庫當中,若是系統爲Windows,那麼數據庫文件所在的位置爲[1]:github
C:\Users\{UserName}\AppData\Local\Google\Chrome\User Data\Default\Cookies
其中該路徑的{UserName}是當前系統的用戶名。sql
username = os.environ.get('USERNAME') cookie_file = 'C:\Users\{UserName}\AppData\Local\Google\Chrome\User Data\Default\Cookies'.format(UserName=username)
經過Cookie文件路徑,咱們能夠創建數據庫鏈接,而後提取出數據庫的信息。數據庫
con = sqlite3.connect(cookie_file) cursor = con.cursor()
數據庫中的cookies表就是用於保存瀏覽器Cookie的。提取表中各列的名稱windows
cursor.execute('SELECT * FROM cookies') for description in cursor.description: print(description[0])
各字段的描述以下api
上述字段中有些是隻有瀏覽器纔會用到的,這裏沒有必要用上,所以咱們僅須要提取一些必要字段瀏覽器
cursor.execute('SELECT host_key, name, value, path, expires_utc, is_secure, encrypted_value ' 'FROM cookies WHERE host_key like "%{}%";'.format(domain_name))
咱們前面說過,Chrome在對Cookie的值保存以前會進行加密處理,並保存在數據庫的encrypt_value字段中。在Windows系統中,Cookie加密採用的是系統提供的函數CryptProtectData,咱們在解密的時候也須要調用系統提供的函數CryptUnprotectData[1]。解密的Windows用戶必須與加密的用戶一致才能成功解密。安全
不過系統提供的是C函數,python經過ctypes庫來實現對C函數的調用。
CryptUnprotectData須要7個參數:
class DATA_BLOB(ctypes.Structure): _fields_ = [("cbData", ctypes.wintypes.DWORD), ("pbData", ctypes.POINTER(ctypes.c_char))] def __init__(self, data): string = str(data) self.cbData = len(string) self.pbData = ctypes.create_string_buffer(string) def descrypt(cipher): #parameters DataIn = DATA_BLOB(cipher) Descr = ctypes.c_wchar_p() DataEntropy = DATA_BLOB('') Reserved = None PromptStruct = None CRYPTPROTECT_UI_FORBIDDEN = 0x00 DataOut = DATA_BLOB('') #win call ret = ctypes.windll.crypt32.CryptUnprotectData(ctypes.byref(DataIn), Descr, ctypes.byref(DataEntropy), Reserved, PromptStruct, CRYPTPROTECT_UI_FORBIDDEN, ctypes.byref(DataOut) ) if not ret: raise RuntimeError("failed to descrypt") buf = ctypes.create_string_buffer(int(DataOut.cbData)) ctypes.memmove(buf, DataOut.pbData, DataOut.cbData) ctypes.windll.kernel32.LocalFree(Descr) ctypes.windll.kernel32.LocalFree(DataOut.pbData) return buf.value
python並不推薦咱們去自行建立Cookie,由於做爲用戶一般不須要去改動,甚至沒有必要知道Cookie的內容,不過此處出於特殊的需求,須要經過在Cookie數據庫中獲取的數據來建立Cookie。
首先咱們來簡單瞭解一下Set-Cookie。咱們知道Cookie都是服務器爲瀏覽器設置的,設置Cookie是經過服務器返回的Response Header,若是header中包含有Set-Cookiie相關字段,則能進行Cookie的設置。
http.cookiejar.Cookie的初始化須要提供18個參數[2]:
cj = http.cookiejar.CookieJar() for row in cursor.fetchall(): host, name, value, path, expires, secure, encrypted_value = row[:] data = descrypt(encrypted_value) c = http.cookiejar.Cookie(0, name, data, None, False, host, host.startswith('.'), host.startswith('.'), path, True, secure, expires, False, None, None, {}) cj.set_cookie(c)
其中比較重要的domain相關參數請查看:rfc2109的Interpreting Set-Cookie、Rejecting Cookies以及rfc6265的The Domain Attribute、Storage Model
Reference: