1. 程式人生 > 其它 >python學習筆記——爬蟲前置——HTTP協議簡介

python學習筆記——爬蟲前置——HTTP協議簡介

Http協議簡介

因為最近剛剛接觸了python爬蟲,想要系統的學習一下,在初次使用requests庫時有一些無法理解的地方,於是就去簡要了解了一點點http協議的基礎知識。

Hyper Text Transfer Protocol 超文字傳輸協議

基於 TCP/IP 協議簇來傳遞資料,位於應用層的協議之一

TCP建立連線——三次握手

  1. 客戶端(client)傳送位碼syn=1,隨機產生 seq_number 的資料包到伺服器(server),伺服器由 syn=1 瞭解該客戶端要求建立連線
  2. 伺服器收到請求後,向客戶端傳送 ack_number = seq_number+1,syn=1,ack=1,隨機產生 seq_number 的資料包到客戶端
  3. 客戶端收到資料包,通過 ack_number 確定正確,以及確認 ack 是否為1;均滿足後客戶端傳送 ack_number = seq_number+1,ack=1到伺服器,伺服器收到後確認ack_number和ack=1正確後,建立連線
sequenceDiagram 客戶端 ->> 伺服器: syn = 1, seq_number(client) = 9876543210 (客戶端隨機生成) Note left of 伺服器:由 syn=1 確認客戶端想要建立連線 伺服器 ->> 客戶端: syn = 1, ack = 1<br/>ack_number(server) = seq_number(client) + 1 = 9876543211<br/>seq_number(server) = 1234567890(伺服器隨機生成) Note right of 客戶端:確定 ack = 1<br/>seq_number(server) = seq_number(client) + 1 客戶端 ->> 伺服器: ack = 1<br/>ack_number(client) = seq_number(server) + 1 =1234567891 Note left of 伺服器:確定 ack = 1<br/>ack_number(client) = seq_number(server) +1 Note over 客戶端,伺服器:連線建立成功

客戶端和伺服器就像我們打電話時,客戶端先問:“你聽得到嗎?”伺服器回答:“聽得到,你呢?”客戶端說:“我聽得到。”然後再開始對話。

Http message (Http報文)

所有 Http報文 都可以分成兩類:request message (請求報文) 和 response message (響應報文)。請求報文會向Web伺服器請求一個動作,響應報文會將請求的結果返回給客戶端。請求和相應報文的基本報文結構相同。

Http Requests (Http請求)

在客戶端與伺服器三次握手成功後,客戶端就可以向伺服器傳送 Requests請求,請求報文的格式如下:

<method> <request-URL> <version>
<headers>

<entity-body>

比如在Edge瀏覽器中訪問百度時,傳送的第一個請求如下:

由 burpsuite 抓包獲得

GET / HTTP/1.1
Host: www.baidu.com
Sec-Ch-Ua: "Chromium";v="92", " Not A;Brand";v="99", "Microsoft Edge";v="92"
Sec-Ch-Ua-Mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.73
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5
Connection: close

我們對該請求進行逐步分析:

Request Attributes(請求屬性)

第一行是 請求行:

GET / HTTP/1.1

其中 GET 是請求的方法(Method); / 是請求的路徑(URL); HTTP/1.1 表明該請求是基於 HTTP1.1 版本(Version)。

Http method

有一些常用的 http 方法:

方法 描述
GET 從伺服器獲取一份文件
HEAD 只從伺服器獲取文件的首部
POST 向伺服器傳送需要處理的資料
PUT 將請求的主體部分儲存在伺服器上
TRACE 對可能經過代理伺服器傳送到伺服器上的報文進行追蹤
PATCH 向伺服器提交區域性修改請求
DELETE 從伺服器上刪除一份文件

在較早的http版本中,一些方法會缺失。

除了上述方法之外,伺服器還可能會實現一些自己的請求方法。

URL

URL 是瀏覽器尋找資訊時所需的資源位置,常見的URL語法如下:

<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
元件 描述
scheme 訪問伺服器時使用的協議,如https協議
user 伺服器的使用者名稱,預設為匿名
password 伺服器的密碼
host 伺服器的主機名或ip地址
port 伺服器正在監聽的埠號,Http的預設埠號為80,Https的預設埠號為443
path 伺服器上資源的本地名
params 指定輸入引數,為鍵值對
query 傳遞引數,為鍵值對
frag 資源片段的名字

如在百度中搜索python時,url如下:

https://www.baidu.com/s?wd=python

其中

  • scheme = https
  • user 省略,則預設為匿名
  • host = www.baidu.com
  • port 省略,則取 https的預設埠號 443
  • path = /s
  • params 省略
  • query = 'wd=python'
  • frag 省略

這也是我們見到的大多數https的 url格式:

https://<host>/<path>?<query>

Http version

常見的http版本有:

  • HTTP 0.9
  • HTTP 1.0
  • HTTP 1.1
  • HTTP 2.0

(感覺基本上都是 http 1.1 和 http 2.0 了)

Request Headers(請求首部)

從第二行開始到結尾,就都是 請求的首部:

Host: www.baidu.com
Sec-Ch-Ua: "Chromium";v="92", " Not A;Brand";v="99", "Microsoft Edge";v="92"
Sec-Ch-Ua-Mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.73
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5
Connection: close

首部都是 "名字:值" 的形式,如:

Host: www.baidu.com

即伺服器的域名為 www.baidu.com,也因此請求訊息中的URL並沒有傳遞主機名。之所以這樣設計是因為虛擬主機技術的發展,使得同一臺物理伺服器上可以存在多個虛擬主機,而他們共享一個ip地址。使用host欄位傳遞主機名,就可以將請求發往同一個伺服器上的不同網站。

如:

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.73

User-Agent這一項在爬蟲中修改較多,該欄位是告訴伺服器使用者是使用何種工具傳送的請求。這個示例是edge瀏覽器的值,不同的瀏覽器有不同的值。

Http Response (Http響應)

在接收到客戶端傳送的 request 請求後,伺服器端會返回一個 response相應給客戶端。響應報文的格式如下:

<version> <status> <reason-phrase>
<headers>

<entity-body>

例如在傳送完上述請求給百度伺服器後,返回的 response 如下:

burpsuite 抓包獲取

HTTP/1.1 200 OK
Bdpagetype: 1
Bdqid: 0xa6f9ca590000f9cd
Cache-Control: private
Content-Type: text/html;charset=utf-8
Date: Mon, 16 Aug 2021 10:02:09 GMT
Expires: Mon, 16 Aug 2021 10:01:13 GMT
P3p: CP=" OTI DSP COR IVA OUR IND COM "
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Server: BWS/1.1
Set-Cookie: BAIDUID=DFF935C15B376DD72F4785A559548401:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BIDUPSID=DFF935C15B376DD72F4785A559548401; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1629108129; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BAIDUID=DFF935C15B376DD7F38BDD53843DD693:FG=1; max-age=31536000; expires=Tue, 16-Aug-22 10:02:09 GMT; domain=.baidu.com; path=/; version=1; comment=bd
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=34399_34380_34370_34377_34004_34092_34094_26350_34419_34246_34390_34366; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 1629108129043925146612031870363097954765
X-Frame-Options: sameorigin
X-Ua-Compatible: IE=Edge,chrome=1
Connection: close
Content-Length: 308542

<html程式碼塊>

Response Attributes(響應屬性)

第一行是響應的屬性:

HTTP/1.1 200 OK

其中 HTTP/1.1 是HTTP協議的版本,與請求中的版本一致; 200 是狀態碼,這裡表示操作成功; OK 為狀態碼200提供了文字形式的解釋,表明操作成功。

stauts(狀態碼)

狀態碼可以告訴客戶端,在請求資料過程中發生了什麼事情。狀態碼的分類如下:

整體範圍 已定義範圍(可能不完整) 分類
100 ~ 199 100 ~ 102 資訊提示
200 ~ 299 200 ~ 208 成功
300 ~ 399 300 ~ 308 重定向
400 ~ 499 400 ~ 417、421、422、424、425、426 客戶端錯誤
500 ~ 599 500 ~ 508、511 伺服器錯誤

如果收到了不認識的狀態碼,可能是有人將其作為當前協議的擴充套件定義的,可以根據其所處範圍來判斷分類

reason-phrase(原因短語)

為狀態碼提供文字形式的解釋,如 狀態碼200 對應的 原因短語 是 "OK",狀態碼404 對應的 原因短語 是"Not Found"

Response Headers(響應首部)

響應的首部 和 請求的首部 類似,均是採用鍵值對的形式,不同鍵值對之間以換行符('\n')隔開。

entity-body(實體部分)

該部分是可選部分,在 request 和 response 中均可存在。

在本次示例response中,實體(entity-body)部分為百度首頁的html程式碼:

    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <meta content="always" name="referrer">
        <meta name="theme-color" content="#2932e1">
        <meta name="description" content="全球領先的中文搜尋引擎、致力於讓網民更便捷地獲取資訊,找到所求。百度超過千億的中文網頁資料庫,可以瞬間找到相關的搜尋結果。">
        <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
        <link rel="search" type="application/opensearchdescription+xml" href="/content-search.xml" title="百度搜索" />
        <link rel="icon" sizes="any" mask href="//www.baidu.com/img/baidu_85beaf5496f291521eb75ba38eacbd87.svg">
        <link rel="dns-prefetch" href="//dss0.bdstatic.com"/><link rel="dns-prefetch" href="//dss1.bdstatic.com"/>
        <link rel="dns-prefetch" href="//ss1.bdstatic.com"/><link rel="dns-prefetch" href="//sp0.baidu.com"/>
        <link rel="dns-prefetch" href="//sp1.baidu.com"/><link rel="dns-prefetch" href="//sp2.baidu.com"/>
        <title>百度一下,你就知道</title>
        <!--省略:css檔案-->
      </head>

      <!--太長省略-->
    </html>

除了 html程式碼之外,視訊、音訊、json檔案、……等多種檔案均可置於實體部分中,作為request中上傳的資料或response中獲取的資料。

TCP斷開連線——四次揮手

  1. 客戶端(client)傳送位碼fin=1,隨機產生 seq_number = a 的資料包到伺服器(server),伺服器由 fin=1 瞭解該客戶端要斷開連線
  2. 伺服器收到請求後,向客戶端傳送 ack_number = a+1,ack=1,隨機產生 seq_number = b 的資料包到客戶端,表面收到了客戶端的斷開連線請求
  3. 伺服器隨後傳送位碼fin=1,傳送 ack_number = a+1,ack=1,隨機產生 seq_number = c 的資料包到客戶端
  4. 客戶端收到資料包,通過 ack_number 確定正確,以及確認 ack和fin 是否為1;均滿足後客戶端傳送 ack_number = c+1,seq_number = a+1, ack=1到伺服器,伺服器收到後確認ack_number和ack=1正確後,斷開連線
  5. 客戶端等待一定時間後,若伺服器無報文到達,則說明自己的響應成功到達伺服器端,客戶端也斷開連線
sequenceDiagram 客戶端 ->> 伺服器: fin = 1, seq_number = a (客戶端隨機生成) Note left of 伺服器:由 fin=1 確認客戶端想要斷開連線 伺服器 ->> 客戶端: ack = 1<br/>ack_number = a + 1<br/>seq_number = b(伺服器隨機生成) Note right of 客戶端:確定伺服器收到請求<br/>等待伺服器傳送斷開通知 伺服器 ->> 客戶端: fin = 1, ack = 1<br/>ack_number = a + 1<br/>seq_number = c(伺服器隨機生成) 客戶端 ->> 伺服器: ack = 1<br/>ack_number = c + 1<br/>seq_number = a + 1 Note left of 伺服器:確定 ack = 1<br/>確定ack_number和seq_number的值<br/>伺服器斷開連線 Note right of 客戶端:等待一段時間後,未接受到伺服器的資訊<br/>則認為伺服器已經斷開連線<br/>客戶端也斷開連線 Note over 客戶端,伺服器:連線斷開

仍然存在的一些問題

在學習過程中,仍然存在一些問題,暫時無法理解,先記錄下來,今後的學習中有機會再來弄懂。

  • 百度主頁的url是 https://www.baidu.com/ , 主機名(host) 是 www.baidu.com,資源的本地名(path) 是 /;但是 / 是伺服器根目錄,並不是一個檔案,是伺服器自動重定向到index.html之類的檔案嗎?
  • Mozilla/5.0、AppleWebKit/537.36、……等都是瀏覽器核心的版本號,為什麼edge瀏覽器傳送的user-agent要包括它沒用到的核心?