前言
在 Python 中,訪問網絡資源最有名的庫就是 requests、aiohttp 和 httpx。一般情況下,requests 只能發送同步請求;aiohttp 只能發送異步請求;httpx 既能發送同步請求,又能發送異步請求。
下面,就著重介紹一下 httpx 的使用方法。
安裝
使用 pip 安裝 httpx:
pip install httpx
當然了,httpx 也可以使用命令行操作。不過,需要按如下命令安裝。
pip install 'httpx[cli]'
命令行測試發送請求:
快速入門
發起 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:8030 和 http://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 塊會在退出時自動關閉響應。