Python httpx:高效能的次世代同步與非同步 HTTP 用戶端

在 Python 的世界裡,requests 長期以來都是處理 HTTP 請求的首選。然而,隨著 AsyncIO 非同步程式設計的普及,requests 因為不支援非同步操作,在處理高併發任務時顯得有些力不從心。

HTTPX 是一個完全相容於 requests API 介面,且原生支援 非同步 (Async)HTTP/2 的次世代 HTTP 用戶端函式庫。

為什麼選擇 HTTPX?

  1. 高度相容:它的 API 幾乎與 requests 一模一樣,轉換成本極低。
  2. 原生非同步:提供了 AsyncClient,能完美整合到 FastAPI 或其他非同步框架中。
  3. HTTP/2 支援:支援現代化的協定,能提升連線效率。
  4. 強型別支援:更符合現代化的開發習慣。

安裝 HTTPX

pip install httpx

如果你需要 HTTP/2 支援,則安裝:

pip install httpx[http2]

基礎語法:同步請求 (與 requests 幾乎相同)

如果你習慣 requests,你可以直接上手 httpx

import httpx

# 同步 GET 請求
response = httpx.get("https://httpbin.org/get", params={"key": "value"})

print(f"狀態碼: {response.status_code}")
print(f"回應內容: {response.json()}")

# 同步 POST 請求
payload = {"name": "httpx"}
response = httpx.post("https://httpbin.org/post", json=payload)

核心亮點:非同步請求 (AsyncClient)

這是 httpx 最強大的地方。在非同步環境(如 FastAPI)中,你必須使用 AsyncClient

import httpx
import asyncio

async def fetch_data():
    # 使用 AsyncClient 作為上下文管理器
    async with httpx.AsyncClient() as client:
        response = await client.get("https://httpbin.org/get")
        print(f"Async Response: {response.status_code}")

async def main():
    # 同時發送多個請求
    tasks = [fetch_data() for _ in range(5)]
    await asyncio.gather(*tasks)

if __name__ == "__main__":
    asyncio.run(main())

使用 Client 提升效能 (Connection Pooling)

無論是同步還是非同步,如果你需要對同一個伺服器發送多次請求,建議使用 Client 物件。這會啟用 連線池 (Connection Pooling),重複使用傳輸層連線,大幅提升效能。

同步版本

with httpx.Client() as client:
    for i in range(10):
        r = client.get(f"https://example.com/api/{i}")

非同步版本

async with httpx.AsyncClient() as client:
    # 這裡的連線會被重用
    r = await client.get("https://example.com")

HTTPX vs Requests 比較表

特性RequestsHTTPX
API 設計非常人性化延續 Requests 風格 (高度相似)
非同步支援❌ 需搭配 grequests 等套件✅ 原生支援 AsyncClient
HTTP/2 支援❌ 不支援✅ 支援
強型別 / Type Hints基礎✅ 完整支援
預設超時❌ 預設不超時 (容易卡死)✅ 預設 5 秒超時 (更安全)

超時與錯誤處理

httpx 非常重視安全性,預設情況下所有請求都有 5 秒的超時限制(requests 預設是無限期等待)。

import httpx

try:
    # 自訂超時
    response = httpx.get("https://example.com", timeout=10.0)
    # 檢查狀態碼,若非 2xx 則拋出異常
    response.raise_for_status()
except httpx.TimeoutException:
    print("請求超時了!")
except httpx.HTTPStatusError as exc:
    print(f"HTTP 錯誤回傳: {exc.response.status_code}")

總結

  • 如果你的專案是純同步的,requests 依然很好用。
  • 如果你正在開發 FastAPI 或任何非同步應用,HTTPX 是你的唯一首選
  • 它不僅提供了與 requests 一樣好用的介面,還帶來了 HTTP/2 與更安全的預設超時設定。
學習更多關於 FastAPI 的並發機制,你會更了解為什麼在非同步框架中必須使用 httpx 而不是 requests