那些年,我爬過的北科(二)——爬蟲基礎之session登錄

(注:因爲如今域名全都要備案了,.tech 域名不讓備案,下面的nladuo.tech 統一更改成 nladuo.cnphp

說說HTTP請求:GET與POST

在上一節中,咱們在不知道原理的條件下調用了requests.get方法下載了HTML頁面。在本節中,咱們來講說什麼是HTTP請求和它的特色。html

在HTTP請求中,主要有GET和POST兩種方式,其主要區別在於:linux

  • GET的信息存儲在url中,好比說咱們在上節看到的「?categoryId=1」。
  • 而POST的信息則把信息存儲在form中,好比說咱們在輸入登錄用戶名密碼的時候,不會在網址中看到這些隱私信息;固然,咱們在上傳大文件的時候,好比說上傳一個1個G的視頻,也不會把視頻的信息放到url中。

另外,HTTP是一種短鏈接的協議,它基於TCP。爲何這麼說呢,由於HTTP請求的過程其實就是:git

首先,咱們的瀏覽器經過tcp和遠程web服務器的端口互相創建鏈接;而後發送一個指令,好比說要獲取根鏈接(url爲'/')的內容;再而後服務器會根據客戶端(咱們的瀏覽器)的請求返回相應的結果(如HTML文本、圖片等);客戶端獲得了請求以後,就會將鏈接關閉掉,同時web服務器也關閉和客戶端的鏈接;這樣,一次HTTP請求也就完成了。github

那麼瀏覽器和web服務器是如何發送指令的呢?web

在Chrome中查看HTTP請求

下面以nladuo.cn/test.html來做爲測試頁面,在輸入連接的同時打開Chrome的開發者工具,調整到Network選項。 chrome

這個網頁只有一個請求,那就是它的HTML,返回「It Works」。下面,咱們在請求列表中點擊test.html這項,能夠看到服務器的相應頭Requests Headers和客戶端的請求頭Response Headers。 數據庫

咱們點擊Requests Headers邊上的view source能夠看到原始的請求頭。 瀏覽器

在這裏面能夠看到:緩存

  • 請求了nladuo.cn上面的「/test.html」這個資源,使用了HTTP1.1版本的協議,
  • 請求的域名的Host爲: nladuo.cn
  • 等等。。。

同理查看Response Headers,

能夠看到:

  • 使用了HTTP1.1版本的協議,返回碼爲200 OK
  • 日期是啥
  • 服務器是Apache,2.4.7(Ubuntu)版本
  • 等等。。。

下面咱們來使用電腦自帶的tcp客戶端telnet來模擬發送GET請求,看看一個GET請求的實際流程。

在電腦中使用telnet客戶端

對於mac或者linux用戶,能夠不用進行任何配置,打開終端輸入telnet便可使用。

對於Windows用戶,能夠在此處查看開啓telnet部分。

使用telnet模擬GET請求

對於網站來講,默認開放的是80端口,好比在Network請求中的General中能夠雖然沒有配置,但訪問的是80端口。固然,你也能夠輸入nladuo.cn:80/test.html進行訪問,不過通常都不會畫蛇添足。

因此咱們要用tcp連接nladuo.cn的80端口,使用如下命令。

telnet nladuo.cn 80
複製代碼

輸入以後,就能夠看到nladuo.cn被解析成了ip:123.206.86.230(這裏換了個服務器,因此ip變了,原爲191.101.13.124)了,並創建起了tcp鏈接。在這時mac和linux用戶就能夠準備輸入命令了;對於Windows系統須要首先摁下ctrl鍵 + ']' 鍵進入輸入模式,而後再按回車切換到顯式輸入模式後再輸入命令。

咱們像在chrome的Network看到的同樣,首先咱們要發一個GET請求訪問/test.html資源,並使用HTTP1.1版本的協議,而後再告訴服務器咱們訪問的Host是nladuo.cn。以後,咱們還能夠告訴服務器User-Agent是啥,Accept-Encoding是啥,不過這些都不是最重要的,因此只要輸入前兩條便可。

GET /test.html HTTP/1.1(回車)
Host: nladuo.cn(回車+回車)
複製代碼

在這裏,每輸入一條後輸入一個回車,在最後輸入兩個回車。在輸入兩個回車以後,等待片刻,就能夠看到服務器給咱們返回的信息了。

這裏能夠看到像以前Chrome瀏覽器中看到的同樣,返回了200 OK、時間、服務器信息、等等....

在相應頭後面的兩個回車後面,能夠看到返回的HTML信息:「It Works」

到這裏,此次請求就已經結束了,再等待片刻,能夠看到遠程服務器關閉了tcp連接。一次HTTP請求也正式完成了。

模擬登錄

登錄過程發生了什麼?

經過上面的利用tcp模擬http請求的案例,咱們知道了客戶端發送一個請求頭信息,服務器返回一個相應頭信息+HTML後,tcp鏈接就關閉了。可是咱們平常使用網頁中登錄網頁以後,刷新以後瀏覽器還能記得咱們登錄的信息,這是什麼原理呢?

下面以一個簡單的登錄頁面來學習一下登錄過程當中都發生了什麼?登錄的連接地址爲:nladuo.cn/crawler_les…。而登錄後的隱私連接地址爲:nladuo.cn/crawler_les…

首先,咱們先不登陸,訪問一下隱私頁面,注意要首先打開Network,再輸入連接nladuo.cn/crawler_les…

這裏能夠看到網頁顯示出了無權查看,而且Response Headers多了一個Set-Cookie字段。固然,若是你沒按照我說的作,而是先輸入url,顯示出頁面,而後再打開Network,刷新一下頁面查看的話,就會看到如下的結果。

在Response Headers的Set-Cookie不見了,而Requests Headers多了一個Cookie,這兩個的值仍是同樣的,都是「PHPSESSID=9m8vgq9699fun79t3ks6ljrdh7」。

固然,這個時候無論再刷新幾回頁面,Response Headers都沒有出現新的Set-Cookie了;而在第一次的以後全部請求,Request Headers都會帶着一個Cookie。好比咱們這裏查看登錄頁面,nladuo.cn/crawler_les…,也帶了這個Cookie。

這裏能夠先帶着疑問,咱們如今知道,Set-Cookie只執行一次,由服務器返回;在此以後,瀏覽器會保存Set-Cookie保存的值,每次訪問這個域的內容都會帶着Set-Cookie的值,並把他放到Cookie字段裏。

接下來,咱們嘗試一下登錄,這裏,用戶名和密碼都默認爲nladuo,咱們只要在輸入框輸入nladuo便可。點擊登錄後,咱們能夠看到這裏發了一個POST請求,顯示出了「登錄成功」。

相比GET而言,POST的請求頭中多了Content-Type和Content-Length兩個字段,由於此次是把數據放到了Form Data中,而不是在url裏了。這裏Content-Length就是發送字節的長度,Content-Type則是發送的類型,這裏會把表單中的字段轉換成鍵值對,如uname:nladuo,passwd=nladuo轉換爲uname=nladuo&passwd=nladuo,這樣正好也是26個字符。

固然,這裏,咱們也能夠用Telnet模擬POST請求,讀者能夠看一下,這裏就很少說了。

下面,咱們再看一下Cookie,能夠發現這裏Cookie沒有變,仍是 「PHPSESSID=9m8vgq9699fun79t3ks6ljrdh7」。

Cookie與Session

看到這裏,帶着疑問,能夠引出Cookie和Session的概念了。

咱們上面看到的Cookie是一種客戶端的技術,而Session則是服務端的技術。登錄的過程其實就是一個會話開始的時候,服務器給客戶端一個ID,如上面看到的Set-Cookie,這其實就表明會話的開始,而當瀏覽器關閉後,一次會話也就結束了。

在一次會話中,用戶的狀態保存在服務器中,而客戶端是保存一個會話ID,因此當咱們在沒登錄的時候請求nladuo.cn/crawler_les…頁面時,服務器會在緩存數據庫裏的PHPSESSID這張表查找id爲9m8vgq9699fun79t3ks6ljrdh7的字段,判斷用戶是否登錄了,而後根據查詢的結果來返回不一樣的頁面。

好比,咱們在沒有登錄前,緩存服務器中PHPSESSID中9m8vgq9699fun79t3ks6ljrdh7的is_login字段是NULL,因此服務器查詢到is_login是空的,因此不給用戶看隱私頁面。當咱們登錄的時候,若是用戶名密碼正確,服務器就會給緩存服務器上的9m8vgq9699fun79t3ks6ljrdh7字段的is_login設置爲True,當用戶下次請求隱私頁面的時候,就能夠看到正確的返回結果了。

使用Cookie模擬登錄

經過上面的學習,咱們知道了如何進行登陸,下面咱們用代碼模擬一下這個步驟:1.首先登錄,2.保存Cookie,3.帶着Cookie請求隱私頁面

# 1. 先登錄
resp1 = requests.post("http://nladuo.cn/crawler_lesson2/do_login.php",
  data={
    "uname": "nladuo",
    "passwd": "nladuo"
})

# 2. 保存服務器傳回來的Cookie
print("Set-Cookie:", resp1.headers["set-cookie"])
cookie = resp1.headers["set-cookie"].split(";")[0]

# 3. 再經過cookie請求隱私頁面
resp = requests.get("http://nladuo.cn/crawler_lesson2/private.php",
  headers={
    "Cookie":  cookie  # 現用瀏覽器或者Telnet發送Post請求登陸, 把cookie粘到這裏
})
print(resp.content)
複製代碼

運行代碼,能夠看到這裏登錄成功,返回了隱私頁面。

使用requests.session模擬登錄

不過,上面的方式手動管理Cookie總感受有些麻煩,Requests庫給我提供了一個更方便的對象:requests.session,它能夠像瀏覽器同樣記錄咱們的會話中的Cookie。

import requests

# 1. 建立一個Session
session = requests.session()
# 2. 登錄
session.post("http://nladuo.cn/crawler_lesson2/do_login.php",
             data={"uname": "nladuo", "passwd": "nladuo"})
# 3. 訪問隱私頁面
resp = session.get("http://nladuo.cn/crawler_lesson2/private.php")
print(resp.content)
複製代碼

如今,咱們的代碼中的流程就像人用瀏覽器的操做的過程同樣了,登錄一下,直接請求隱私頁面就行了。

運行代碼後,能夠看到一樣的正確結果。

相關文章
相關標籤/搜索