詳解Python中httptools模塊的使用
如果你用過 FastAPI 的話,那么你一定知道 uvicorn,它是一個基于 uvloop 和 httptools 實現(xiàn)的高性能 ASGI 服務(wù)器。
其中 uvloop 采用 Cython 編寫,用于替換 asyncio 中的事件循環(huán),可以讓 asyncio 速度增加 2 到 4 倍。而 httptools 是基于 C 語言實現(xiàn)的 HTTP 解析器,用來解析 HTTP 請求的。
本次就來聊一聊 httptools 這個模塊的詳細用法,至于 uvloop、uvicorn 等相關(guān)內(nèi)容,后續(xù)我會一點一點補充上去,并從源碼的角度全給說明白(挖了個坑)。
httptools 是一個 HTTP 解析器,它首先提供了一個 parse_url 函數(shù),用來解析 URL。
import?httptools #?第一個參數(shù)必須是?bytes?對象 url?=?httptools.parse_url( ????b"http://www.baidu.com" ) #?返回一個?URL?對象 print(url.__class__) """ <class?'httptools.parser.parser.URL'> """
那么這個 URL 對象有哪些屬性呢?

通過源碼可知,總共有七個屬性,我們來測試一下。
import?httptools
#?第一個參數(shù)是?bytes?對象
url?=?b"http://satori:123456@www.baidu.com:80/s?wd=koishi#flag"
url_obj?=?httptools.parse_url(url)
print("協(xié)議:",?url_obj.schema)
print("IP:",?url_obj.host)
print("端口:",?url_obj.port)
print("路徑:",?url_obj.path)
print("查詢參數(shù):",?url_obj.query)
print("錨點:",?url_obj.fragment)
print("用戶信息:",?url_obj.userinfo)
"""
協(xié)議:?b'http'
IP:?b'www.baidu.com'
端口:?80
路徑:?b'/s'
查詢參數(shù):?b'wd=koishi'
錨點:?b'flag'
用戶信息:?b'satori:123456'
"""比較簡單,如果參數(shù)不符合 URL 的標(biāo)準(zhǔn)格式,那么會拋出 HttpParserInvalidURLError 錯誤。
然后是 HTTP 請求報文和響應(yīng)報文的解析,因為報文只是一坨字節(jié)流,需要將它解析成某個 Request 對象或 Response 對象,而 httptools 就是干這件事情的。
首先來看一下報文格式,請求報文如下:

接下來是響應(yīng)報文:

所以無論是請求報文還是響應(yīng)報文,都由 起始行 + 請求頭/響應(yīng)頭 + 請求體/響應(yīng)體 組成。而我們在拿到原始的報文之后,也可以很方便地進行解析,從圖中可以看出最后一個 Header 字段和響應(yīng)體之間有兩個換行,而換行用 \r\n 表示。因此我們只要按照 "\r\n\r\n" 進行 split 即可,會得到一個數(shù)組,數(shù)組的第二個元素就是請求體/響應(yīng)體,第一個元素就是起始行 + 請求頭/響應(yīng)頭。
然后對數(shù)組的第一個元素按照 "\r\n" 再進行 split,又可以得到一個數(shù)組,該數(shù)組的第一個元素就是起始行,剩余的元素就是請求頭/響應(yīng)頭。
所以我們在拿到報文之后,完全可以自己手動解析,但 httptools 是用 C 實現(xiàn)的,所以速度會快一些,但干的事情是一樣的。下面來看看 httptools 如何解析請求報文:
from?pprint?import?pprint
import?httptools
#?請求報文
request_payload?=?b"""POST?/index?a=1?HTTP/1.1
Host:?localhost:8080
Connection:?keep-alive
Content-Length:?26
Cache-Control:?max-age=0
Upgrade-Insecure-Requests:?1
Accept:?text/html
Accept-Encoding:?gzip,?deflate,?sdch
Cookie:?_octo=GH1.1.1989111283.1493917476;?logged_in=yes
{"name":"satori","age":17}"""
class?Request:
????"""
????將請求報文的解析結(jié)果封裝成?Request?對象
????"""
????def?__init__(self):
????????self.headers?=?{}
????????self.body?=?b""
????????self.path?=?None
????def?on_url(self,?path:?bytes):
????????self.path?=?path
????def?on_header(self,?name:?bytes,?value:?bytes):
????????self.headers[name]?=?value
????def?on_body(self,?body:?bytes):
????????self.body?=?body
#?實例化?Request?對象
request?=?Request()
#?將?request?作為參數(shù)傳到?HttpRequestParser?中
parser?=?httptools.HttpRequestParser(request)
#?傳入請求報文,進行解析
parser.feed_data(request_payload)
#?獲取?HTTP?版本
print(parser.get_http_version())
"""
1.1
"""
#?是否是長鏈接(Connection?指定為?keep-alive)
print(parser.should_keep_alive())
"""
True
"""
#?獲取請求方法
print(parser.get_method())
"""
b'POST'
"""
#?以上幾個都是?HttpRequestParser?對象的方法
#?獲取路徑
print(request.path)
"""
b'/index?a=1'
"""
#?獲取請求頭
pprint(request.headers)
"""
{b'Accept':?b'text/html',
?b'Accept-Encoding':?b'gzip,?deflate,?sdch',
?b'Cache-Control':?b'max-age=0',
?b'Connection':?b'keep-alive',
?b'Content-Length':?b'26',
?b'Cookie':?b'_octo=GH1.1.1989111283.1493917476;?logged_in=yes',
?b'Host':?b'localhost:8080',
?b'Upgrade-Insecure-Requests':?b'1'}
"""
#?Cookie?也是請求頭的一部分,但在解析的時候會單獨拿出來
#?再解析成一個字典,然后通過?request.cookies?獲取
#?獲取請求體
print(request.body)
"""
b'{"name":"satori","age":17}'
"""以上就是請求報文的解析,再來看看響應(yīng)報文。
from?pprint?import?pprint
import?httptools
#?響應(yīng)報文
response_payload?=?b"""HTTP/1.1?200?OK
Server:?TornadoServer/6.1
Content-Type:?text/html;?charset=UTF-8
Date:?Sun,?22?May?2022?17:54:11?GMT
Content-Length:?21
name:?satori,?age:?17"""
class?Response:
????"""
????將響應(yīng)報文的解析結(jié)果封裝成?Response?對象
????"""
????def?__init__(self):
????????self.headers?=?{}
????????self.body?=?b""
????????self.status?=?b""
????def?on_header(self,?name:?bytes,?value:?bytes):
????????self.headers[name]?=?value
????def?on_body(self,?body:?bytes):
????????self.body?=?body
????def?on_status(self,?status:?bytes):
????????self.status?=?status
#?實例化?Response?對象
response?=?Response()
#?將?response?作為參數(shù)傳到?HttpResponseParser?中
parser?=?httptools.HttpResponseParser(response)
#?傳入響應(yīng)報文,進行解析
parser.feed_data(response_payload)
#?獲取?HTTP?版本
print(parser.get_http_version())
"""
1.1
"""
#?是否是長鏈接(不指定?Connection,默認(rèn)為長連接)
print(parser.should_keep_alive())
"""
True
"""
#?獲取狀態(tài)碼
print(parser.get_status_code())
"""
b'OK'
"""
#?獲取狀態(tài)碼對應(yīng)的描述
print(response.status)
"""
b'OK'
"""
#?獲取響應(yīng)頭
pprint(response.headers)
"""
{b'Content-Length':?b'21',
?b'Content-Type':?b'text/html;?charset=UTF-8',
?b'Date':?b'Sun,?22?May?2022?17:54:11?GMT',
?b'Server':?b'TornadoServer/6.1'}
"""
#?獲取響應(yīng)體
print(response.body)
"""
b'name:?satori,?age:?17'
"""以上就是請求報文和響應(yīng)報文的解析,但如果你不是手動發(fā)送 TCP 請求的話,那么該模塊基本用不到。因為對于任何一個成熟的模塊而言,都具備了報文解析功能。像 requests, httpx, aiohttp 等等,以及一些 web 框架,它們在拿到報文之后會自動解析成某個對象,我們直接通過指定的屬性獲取即可。
而 httptools 便是 uvicorn 的報文解析器,我們在使用 uvicorn 的時候,uvicorn 內(nèi)部也會自動通過 httptools 將報文解析好,而不需要我們手動解析。
因此這里介紹的 httptools 了解一下即可,我們只需要知道它是基于 C 實現(xiàn)的,性能非常高就行。但我們不會手動使用它,而是在使用某個框架(uvicorn)的時候,由框架自動幫我們將報文解析好。
到此這篇關(guān)于詳解Python中httptools模塊的使用的文章就介紹到這了,更多相關(guān)Python httptools模塊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python?Flask框架實現(xiàn)小紅書圖片無水印解析下載
這篇文章主要為大家介紹了Python?Flask框架實現(xiàn)小紅書圖片無水印解析下載,需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11
centos6.8安裝python3.7無法import _ssl的解決方法
這篇文章主要介紹了centos6.8安裝python3.7無法import _ssl的解決方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-09-09
python之broadcast和numpy.sum()函數(shù)用法及說明
這篇文章主要介紹了python之broadcast和numpy.sum()函數(shù)用法及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06
Python OpenCV高斯金字塔與拉普拉斯金字塔的實現(xiàn)
這篇文章主要介紹了Python OpenCV高斯金字塔與拉普拉斯金字塔的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
基于Python實現(xiàn)通過微信搜索功能查看誰把你刪除了
這篇文章主要介紹了基于Python實現(xiàn)微信搜索查看誰把你刪除了的相關(guān)資料,需要的朋友可以參考下2016-01-01

