Python3的下一代HTTP客戶端——HTTPX

前言

在 Python 中,訪問網絡資源最有名的庫就是 requestsaiohttphttpx。一般情況下,requests 只能發送同步請求;aiohttp 只能發送異步請求;httpx 既能發送同步請求,又能發送異步請求。

下面,就著重介紹一下 httpx 的使用方法。

安裝

使用 pip 安裝 httpx:

pip install httpx

當然了,httpx 也可以使用命令行操作。不過,需要按如下命令安裝。

pip install 'httpx[cli]'
Python3的下一代HTTP客戶端——HTTPX-1.png

命令行測試發送請求:


Python3的下一代HTTP客戶端——HTTPX-2.png

快速入門

發起 GET 請求

直接用 get 方法,如下:

import httpx

r = httpx.get('https://httpbin.org/get')
print(r.status_code)    #狀態
print(r.text)   #內容

對于帶參數的 URL,傳入一個 dict 作為 params 參數,如下:

import httpx

r = httpx.get('https://httpbin.org/get', params={'q': 'python', 'cat': '1001'})
print(r.url)    #實際請求的URL
print(r.text)

對于特定類型的響應,例如 JSON,可以直接獲取,如下:

r = httpx.get('https://httpbin.org/get')
r.json()

# {'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': ...

對于非文本響應,響應內容也可以以字節的形式訪問,如下:

>>> r.content
b'<!doctype html>\n<html>\n<head>\n<title>Example Domain</title>...'

添加 Headers

需要傳入 HTTP Header 時,我們傳入一個 dict 作為 headers 參數,如下:

r = httpx.get('https://www.baidu.com/', headers={'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit'})

獲取響應頭,如下:

r.headers
# {Content-Type': 'text/html; charset=utf-8', 'Transfer-Encoding': 'chunked', 'Content-Encoding': 'gzip', ...}

r.headers['Content-Type']
# 'text/html; charset=utf-8'

發起 POST 請求

要發送 POST 請求,只需要把 get()方法變成 post(),然后傳入 data 參數作為 POST 請求的數據,如下:

r = httpx.post('https://accounts.baidu.com/login', data={'form_email': 'abc@example.com', 'form_password': '123456'})

httpx 默認使用 application/x-www-form-urlencoded 對 POST 數據編碼。如果要傳遞 JSON 數據,可以直接傳入 json 參數,如下:

params = {'key': 'value'}
r = httpx.post(url, json=params) #內部自動序列化為JSON

發起二進制請求

content = b'Hello, world'
r = httpx.post("https://httpbin.org/post", content=content)

上傳文件

上傳文件操作如下:

upload_files = {'upload-file': open('report.xls', 'rb')}
r = httpx.post('https://httpbin.org/post', files=upload_files)

如果需要在上傳文件時包含非文件數據字段,請使用 data 參數,如下:

data = {'message': 'Hello, world!'}
files = {'file': open('report.xls', 'rb')}
r = httpx.post("https://httpbin.org/post", data=data, files=files)
print(r.text)

流媒體響應

可以流式傳輸響應的二進制內容:

>>> with httpx.stream("GET", "https://www.example.com") as r:
...     for data in r.iter_bytes():
...         print(data)

或者返回文本:

>>> with httpx.stream("GET", "https://www.example.com") as r:
...     for text in r.iter_text():
...         print(text)

或者逐行流式傳輸:

>>> with httpx.stream("GET", "https://www.example.com") as r:
...     for line in r.iter_lines():
...         print(line)

添加 Cookie

在請求中傳入 Cookie,只需準備一個 dict 傳入 cookies 參數,如下:

cs = {'token': '12345', 'status': 'working'}
r = httpx.get(url, cookies=cs)

httpx 對 Cookie 做了特殊處理,使得我們不必解析 Cookie 就可以輕松獲取指定的 Cookie,如下:

r.cookies['token']
# 12345

指定超時

默認超時為 5 秒。要指定超時,傳入以秒為單位的 timeout 參數。超時分為連接超時和讀取超時,如下:

try:
    # 3.1秒后連接超時,27秒后讀取超時
    r = requests.get(url, timeout=(3.1, 27))
except requests.exceptions.RequestException as e:
    print(e)

當然,也可以禁用超時:

httpx.get('https://github.com/', timeout=None)

超時重連

def gethtml(url):
    i = 0
    while i < 3:
        try:
            html = httpx.get(url, timeout=5).text
            return html
        except httpx.exceptions.RequestException:
            i += 1

重定向

默認情況下,httpx 不會遵循所有 HTTP 方法的重定向,不過可以使用 follow_redirects 開啟重定向:

>>> r = httpx.get('http://github.com/', follow_redirects=True)
>>> r.url
URL('https://github.com/')
>>> r.status_code
200
>>> r.history
[<Response [301 Moved Permanently]>]

高級用法

使用 httpx.Client() ,實際上是調用 HTTP 鏈接池。可以帶來顯著的性能改進。包括:減少跨請求的延遲(無需握手),減少 CPU 使用和往返,減少網絡擁塞。

用法

使用 Client 的推薦方式是作為上下文管理器。這將確保在離開 with 塊時正確清理連接:

with httpx.Client() as client:
    ...

或者,可以使用.close()顯式關閉連接池,而無需使用塊:

client = httpx.Client()
try:
    ...
finally:
    client.close()

發送請求

一旦有了 Client,您可以使用.get().post()等發送請求。例如:

>>> with httpx.Client() as client:
...     r = client.get('https://example.com')
...
>>> r
<Response [200 OK]>

跨請求共享配置

Client 允許您通過將參數傳遞給 Client 構造函數來將配置應用于所有傳出請求:

>>> url = 'http://httpbin.org/headers'
>>> headers = {'user-agent': 'my-app/0.0.1'}
>>> with httpx.Client(headers=headers) as client:
...     r = client.get(url)
...
>>> r.json()['headers']['User-Agent']
'my-app/0.0.1'

此外,base_url 允許您為所有傳出請求預留 URL:

>>> with httpx.Client(base_url='http://httpbin.org') as client:
...     r = client.get('/headers')
...
>>> r.request.url
URL('http://httpbin.org/headers')

監控下載進度

如果您需要監控大響應的下載進度,您可以使用響應流并檢查 responseresponse.num_bytes_downloaded 屬性:

import tempfile
import httpx

with tempfile.NamedTemporaryFile() as download_file:
    url = "https://speed.hetzner.de/100MB.bin"
    with httpx.stream("GET", url) as response:
        print('response', response)
        total = int(response.headers["Content-Length"])
        print('total', total)
        for chunk in response.iter_bytes():
            download_file.write(chunk)
            percent = response.num_bytes_downloaded / total
            print('percent: {:.2%}'.format(percent))

添加代理

httpx 支持通過 proxies 參數設置 HTTP 代理:

with httpx.Client(proxies="http://localhost:8030") as client:
    ...

對于更高級的用例,請傳遞代理 dict。例如,要將 HTTP 和 HTTPS 請求路由到 2 個不同的代理,分別位于 http://localhost:8030http://localhost:8031,請傳遞代理 URL:

proxies = {
    "http://": "http://localhost:8030",
    "https://": "http://localhost:8031",
}

with httpx.Client(proxies=proxies) as client:
    ...

代理憑據可以作為代理 URLuserinfo 部分傳遞:

proxies = {
    "http://": "http://username:password@localhost:8030",
    # ...
}

異步支持

發送異步請求

>>> async with httpx.AsyncClient() as client:
...     r = await client.get('https://www.example.com/')
...
>>> r
<Response [200 OK]>

打開和關閉 Client

使用 with:

async with httpx.AsyncClient() as client:
    ...

顯式關閉客戶端:

client = httpx.AsyncClient()
...
await client.aclose()

流媒體響應

>>> client = httpx.AsyncClient()
>>> async with client.stream('GET', 'https://www.example.com/') as response:
...     async for chunk in response.aiter_bytes():
...         ...

異步響應流方法有:

  • Response.aread()-用于有條件地讀取流塊內的響應。
  • Response.aiter_bytes()-用于將響應內容流化為字節。
  • Response.aiter_text()-用于將響應內容流化為文本。
  • Response.aiter_lines()-用于將響應內容流式傳輸為文本行。
  • Response.aiter_raw()-用于流式傳輸原始響應字節,無需應用內容解碼。
  • Response.aclose()-用于結束回復。您通常不需要這個,因為.stream 塊會在退出時自動關閉響應。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容