Http協議簡介
因為最近剛剛接觸了python爬蟲,想要系統的學習一下,在初次使用requests庫時有一些無法理解的地方,於是就去簡要了解了一點點http協議的基礎知識。
Hyper Text Transfer Protocol 超文本傳輸協議
基於 TCP/IP 協議簇來傳遞數據,位於應用層的協議之一
TCP建立連接——三次握手
- 客戶端(client)發送位碼syn=1,隨機產生 seq_number 的數據包到服務器(server),服務器由 syn=1 了解該客戶端要求建立連接
- 服務器收到請求后,向客戶端發送 ack_number = seq_number+1,syn=1,ack=1,隨機產生 seq_number 的數據包到客戶端
- 客戶端收到數據包,通過 ack_number 確定正確,以及確認 ack 是否為1;均滿足后客戶端發送 ack_number = seq_number+1,ack=1到服務器,服務器收到后確認ack_number和ack=1正確后,建立連接
客戶端和服務器就像我們打電話時,客戶端先問:“你聽得到嗎?”服務器回答:“聽得到,你呢?”客戶端說:“我聽得到。”然后再開始對話。
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斷開連接——四次揮手
- 客戶端(client)發送位碼fin=1,隨機產生 seq_number = a 的數據包到服務器(server),服務器由 fin=1 了解該客戶端要斷開連接
- 服務器收到請求后,向客戶端發送 ack_number = a+1,ack=1,隨機產生 seq_number = b 的數據包到客戶端,表面收到了客戶端的斷開連接請求
- 服務器隨后發送位碼fin=1,發送 ack_number = a+1,ack=1,隨機產生 seq_number = c 的數據包到客戶端
- 客戶端收到數據包,通過 ack_number 確定正確,以及確認 ack和fin 是否為1;均滿足后客戶端發送 ack_number = c+1,seq_number = a+1, ack=1到服務器,服務器收到后確認ack_number和ack=1正確后,斷開連接
- 客戶端等待一定時間后,若服務器無報文到達,則說明自己的響應成功到達服務器端,客戶端也斷開連接
仍然存在的一些問題
在學習過程中,仍然存在一些問題,暫時無法理解,先記錄下來,今后的學習中有機會再來弄懂。
- 百度主頁的url是 https://www.baidu.com/ , 主機名(host) 是 www.baidu.com,資源的本地名(path) 是 /;但是 / 是服務器根目錄,並不是一個文件,是服務器自動重定向到index.html之類的文件嗎?
- Mozilla/5.0、AppleWebKit/537.36、……等都是瀏覽器內核的版本號,為什么edge瀏覽器發送的user-agent要包括它沒用到的內核?