Python httpx 完整指南:高效的 HTTP 客戶端
更新日期: 2025 年 3 月 3 日
本文為 AI 描述優化 api 設計 系列文,第 9 篇:
- 如何設計一個商品描述優化 API?—— 完整指南
- 設計 AI 優化商品描述的 Prompt
- FastAPI:高效且易用的 Python Web 框架
- 介紹 Uvicorn:高效能 ASGI 伺服器
- Uvicorn 監聽 0.0.0.0,但為何 API 只能用 127.0.0.1 訪問?
- Tenacity:強大的 Python 重試機制庫
- FastAPI 建立商品描述優化 API
- 介紹 pytest:Python 測試框架的強大選擇
- Python httpx 完整指南:高效的 HTTP 客戶端 👈進度
- Pytest-Benchmark:高效能測試的基準分析工具
- 深入解析 monkeypatch:Python 測試中的強大工具
- API 自動化測試與效能優化
建議閱讀本文前,先具備 聊天機器人-建議回復 相關系列文的概念。
在 Python 中,發送 HTTP 請求是一個常見的需求,無論是爬取網頁、與 API 服務端溝通,還是進行測試,HTTP 客戶端 都是不可或缺的工具。
傳統上,Python 社群大多使用 requests
庫來處理 HTTP 請求,但 requests
不支援異步(async)操作,在高併發的場景下可能會遇到效能瓶頸。
這時候,httpx
就成為了一個強大的替代方案。
httpx
不僅具備與 requests
相似的 簡單易用 API,還原生支援 同步與異步請求(async/await),並提供更豐富的功能。
本篇文章將詳細介紹 httpx
,包括其安裝、基本用法、異步請求、進階功能與應用場景,讓你可以輕鬆掌握這個高效能的 HTTP 客戶端。
什麼是 httpx
?
httpx
是一個 基於 httpcore
開發的 HTTP 客戶端庫,由 Encode 團隊 開發,它的主要特點如下:
httpx
主要特性
✅ 簡單易用:httpx
的 API 設計類似 requests
,開發者可以輕鬆上手。
✅ 同步 & 異步支持:可以透過 httpx.get()
發送同步請求,或使用 asyncio
搭配 httpx.AsyncClient()
發送異步請求。
✅ 支援 HTTP/1.1 & HTTP/2:內建支援 HTTP/2,可提升請求效率。
✅ 支援 Cookie、認證、會話(Session)等進階功能。
✅ 內建連線池:減少重複建立連線的開銷,提高效能。
✅ 與 pytest
相容:可搭配 pytest-httpx
進行 API 測試。
httpx
的目標是提供一個「比 requests
更強大,但仍然易用」的 HTTP 客戶端,使其適用於 同步 和 異步 環境。
安裝 httpx
在開始使用之前,請先安裝 httpx
:
pip install httpx
如果需要 HTTP/2 支援,可以安裝:
pip install httpx[http2]
安裝完成後,可以在 Python 中匯入 httpx
:
import httpx
現在,我們就可以開始使用 httpx
來發送 HTTP 請求了!
httpx
的基本用法
發送 GET 請求
與 requests
類似,我們可以使用 httpx.get()
來發送 HTTP GET 請求:
import httpx
response = httpx.get("https://jsonplaceholder.typicode.com/posts/1")
print(response.status_code) # 200
print(response.json()) # 解析 JSON 回應
這將會發送一個 GET 請求,並將回應解析成 Python dict
。
發送 POST 請求
data = {"title": "Hello", "body": "World", "userId": 1}
response = httpx.post("https://jsonplaceholder.typicode.com/posts", json=data)
print(response.status_code) # 201(代表成功建立資源)
print(response.json()) # 解析回應內容
與 requests
一樣,httpx
支援 json=
參數來發送 JSON 格式的資料。
httpx
的進階功能
會話(Session)
在某些應用場景中,我們需要與同一個伺服器進行 多次請求(例如請求 API 端點、下載多個檔案等)。
如果每次請求都重新建立一個新的 HTTP 連線,會產生 額外的連線開銷,導致效能降低。
為了提高效能,httpx
提供了 會話(Session),可以透過 httpx.Client()
來重用連線,減少不必要的連線開啟與關閉的動作。
這與 requests.Session()
類似,但 httpx
的 Client
支援更多功能,例如 連線池(Connection Pooling) 和 HTTP/2。
httpx.Client()
的基本用法
import httpx
# 建立一個 HTTP 會話
with httpx.Client() as client:
response = client.get("https://jsonplaceholder.typicode.com/posts/1")
print(response.json()) # 輸出 API 回應
在這段程式碼中:
- 建立
httpx.Client()
物件:client = httpx.Client()
會創建一個 HTTP 會話,在這個會話內所有請求都會共享同一個 TCP 連線。
- 發送 GET 請求:
client.get("https://jsonplaceholder.typicode.com/posts/1")
會向 API 伺服器發送請求,並取得回應。
- 自動關閉會話:
with httpx.Client() as client:
會確保程式執行完畢後,自動關閉會話並釋放資源。
什麼是 TCP 連線?
TCP(Transmission Control Protocol,傳輸控制協議) 是 網路通訊的基礎協議之一,它確保資料在網路上能夠可靠地傳輸。
當你的電腦要與伺服器溝通時(例如瀏覽網站、發送 API 請求),它會透過 TCP 連線 來建立穩定的數據傳輸管道,確保資料完整、順序正確地抵達目的地。
舉個例子,你打開瀏覽器訪問 https://example.com
,你的電腦會:
- 建立一條 TCP 連線:你的電腦與
example.com
伺服器之間建立一條「專屬的溝通管道」。 - 發送 HTTP 請求:你的電腦通過這條 TCP 連線發送請求給
example.com
。 - 接收 HTTP 回應:
example.com
伺服器回應你的請求,將網頁數據傳回來。 - 關閉 TCP 連線:當請求完成後,這條 TCP 連線可能會被關閉,以釋放資源。
什麼是「共享同一個 TCP 連線」?
通常,每次發送 HTTP 請求時,如果沒有使用 會話(Session),httpx
都會重新建立新的 TCP 連線,然後發送請求,請求完成後關閉連線。
這樣做的問題是:
❌ 每次都要重新建立連線,會消耗額外的時間與系統資源。
❌ 如果要發送多個請求,這些請求之間無法共用同一條 TCP 連線,導致效能降低。
為了解決這個問題,我們可以使用 httpx.Client()
來 建立一個 HTTP 會話,在這個會話內,所有的請求都會 共享同一個 TCP 連線,減少不必要的開銷,提高請求效能。
使用 httpx.Client()
來共享 TCP 連線
import httpx
# 建立 HTTP 會話
with httpx.Client() as client:
response1 = client.get("https://jsonplaceholder.typicode.com/posts/1")
response2 = client.get("https://jsonplaceholder.typicode.com/posts/2")
response3 = client.get("https://jsonplaceholder.typicode.com/posts/3")
print(response1.json())
print(response2.json())
print(response3.json())
這段程式碼的運作方式:
httpx.Client()
建立一個 HTTP 會話,在with
區塊內,client
物件會管理 HTTP 連線。- 第一個請求(
client.get()
)建立了一條 TCP 連線,並且 保持開啟。 - 第二個、第三個請求 會 重用同一條 TCP 連線,不會重新建立新的連線。
- 程式離開
with
區塊後,httpx.Client()
會自動關閉會話並釋放資源。
沒有 httpx.Client()
,會發生什麼事?
如果我們不使用 httpx.Client()
,每次請求 httpx.get()
都會重新建立一條新的 TCP 連線,例如:
import httpx
response1 = httpx.get("https://jsonplaceholder.typicode.com/posts/1")
response2 = httpx.get("https://jsonplaceholder.typicode.com/posts/2")
response3 = httpx.get("https://jsonplaceholder.typicode.com/posts/3")
這樣的問題是:
- 每次請求都要重新建立 TCP 連線,浪費系統資源。
- 在高併發場景下(如爬蟲、大量 API 請求)可能會降低效能,因為每次請求都需要 TCP「三次握手」(建立連線的過程),增加延遲。
TCP 連線與 HTTP 連線的關係
你可以把 TCP 連線比喻成「電話通話」,而 HTTP 請求比喻成「傳送訊息」:
- 沒有
httpx.Client()
→ 每次傳送訊息都要 重新撥打電話,然後講完就掛斷,再打下一通電話。 - 使用
httpx.Client()
→ 撥通一次電話,然後 在這通電話內連續傳送多個訊息,等到所有訊息傳完才掛斷。
顯然,第二種方式 更節省時間和資源,這就是「共享同一條 TCP 連線」的概念。
小結
- TCP 連線 是網路通訊的基礎,負責在你的電腦與伺服器之間傳輸 HTTP 數據。
- 如果每次 HTTP 請求都重新建立 TCP 連線,會消耗額外的時間與資源。
- 使用
httpx.Client()
可以讓所有請求共享同一條 TCP 連線,避免不必要的連線開銷,提高效能。 - 適用場景:批量 API 請求、爬蟲、需要頻繁與伺服器溝通的應用程式。
延伸閱讀:Web 由什麼組成?
發送異步(Async)請求
在高併發的應用場景下,例如:
- 爬取大量網頁
- 批次發送 API 請求
- 與 WebSocket 伺服器進行即時通訊
使用 同步請求(同步 httpx.get()
) 會讓程式 逐個請求、等待回應、再發送下一個請求,造成效能瓶頸。
為了提高效能,httpx
提供 異步(async)API,允許我們同時發送多個請求,而不是一個一個等候回應,從而減少等待時間並提升程式執行速度。
httpx.AsyncClient()
的基本用法
import httpx
import asyncio
async def fetch():
async with httpx.AsyncClient() as client:
response = await client.get("https://jsonplaceholder.typicode.com/posts/1")
print(response.json()) # 輸出 API 回應
# 使用 asyncio.run() 來執行異步函式
asyncio.run(fetch())
在這段程式碼中:
- 使用
async with httpx.AsyncClient()
:AsyncClient
是httpx
的 異步版本,可以讓我們用await
來發送請求,而不會阻塞主程式。
- 透過
await client.get()
來發送請求:await
允許程式在等待伺服器回應時,不會卡住 CPU,而是繼續執行其他任務。
- 使用
asyncio.run(fetch())
來執行異步函式:- 在同步環境下,需要
asyncio.run()
來運行異步函式。
- 在同步環境下,需要
發送多個異步請求(高併發)
如果我們需要一次向 多個 API 端點 發送請求,最好的方式是使用 asyncio.gather()
來同時執行多個請求,減少等待時間。
異步發送多個請求
import httpx
import asyncio
async def fetch(url):
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.json() # 回傳 JSON 結果
async def main():
urls = [
"https://jsonplaceholder.typicode.com/posts/1",
"https://jsonplaceholder.typicode.com/posts/2",
"https://jsonplaceholder.typicode.com/posts/3",
]
# 建立多個異步任務
tasks = [fetch(url) for url in urls]
# 使用 asyncio.gather() 來同時執行所有請求
results = await asyncio.gather(*tasks)
print(results) # 輸出所有請求結果
# 執行異步請求
asyncio.run(main())
執行步驟解析
- 建立
fetch(url)
函式來發送單個異步請求async with httpx.AsyncClient()
創建一個異步 HTTP 客戶端。await client.get(url)
會發送 HTTP GET 請求,但不會阻塞主執行緒。return response.json()
會將 API 回應解析成 Python 字典並回傳。
- 在
main()
函式中:- 建立多個 URL 清單。
- 使用 列表推導式(List Comprehension) 建立 多個
fetch()
任務。 - 用
asyncio.gather(*tasks)
並行執行所有請求,等待它們全部完成後回傳結果。
- 透過
asyncio.run(main())
執行所有請求asyncio.run()
會執行main()
,確保所有請求同時發送,而不是一個一個等待回應後再發送下一個請求。
為什麼要使用 asyncio.gather()
?
✅ 並行執行多個請求,大幅提升速度。
✅ 減少 API 等待時間,因為所有請求是 同時發送 而不是 依序發送。
✅ 適合爬蟲、高頻 API 請求等應用。
httpx
vs requests
:該選哪一個?
特性 | httpx | requests |
---|---|---|
同步請求 | ✅ 支援 | ✅ 支援 |
異步請求 | ✅ 支援 | ❌ 不支援 |
HTTP/2 | ✅ 支援 | ❌ 不支援 |
內建連線池 | ✅ 有 | ❌ 無 |
擴展性 | ✅ 強 | ✅ 強 |
如果你的應用只需要同步請求,requests
依然是好選擇;但如果你需要 異步、HTTP/2、連線池,httpx
會是更好的選擇!
結論
httpx
是requests
的強化版,支援 同步 & 異步請求,適用於一般與高併發應用。- 內建 HTTP/2、連線池、測試工具,適合用於 API 開發、爬蟲等應用場景。
- 若只需同步請求,可繼續使用
requests
;但如果需要異步或高效能,建議使用httpx
! 🚀
希望這篇文章能幫助你快速掌握 httpx
,讓你的 Python 開發更高效!