溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python中httptools模塊如何使用

發(fā)布時間:2023-03-09 14:42:08 來源:億速云 閱讀:133 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“Python中httptools模塊如何使用”,在日常操作中,相信很多人在Python中httptools模塊如何使用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”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 是一個 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 對象有哪些屬性呢?

Python中httptools模塊如何使用

通過源碼可知,總共有七個屬性,我們來測試一下。

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 的標準格式,那么會拋出 HttpParserInvalidURLError 錯誤。

然后是 HTTP 請求報文和響應(yīng)報文的解析,因為報文只是一坨字節(jié)流,需要將它解析成某個 Request 對象或 Response 對象,而 httptools 就是干這件事情的。

首先來看一下報文格式,請求報文如下:

Python中httptools模塊如何使用

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

Python中httptools模塊如何使用

所以無論是請求報文還是響應(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,默認為長連接)
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模塊如何使用”的學習就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI