快轉到主要內容
  1. 教學文章/

httpx:Python 新世代 HTTP 客戶端完全攻略

·4 分鐘· loading · loading · ·
Python Httpx HTTP Async Requests
每日拍拍
作者
每日拍拍
科學家 X 科技宅宅
目錄
Python 學習 - 本文屬於一個選集。
§ 15: 本文

一. 前言
#

嗨,大家好!我是拍拍君 🎉

你是不是每次寫 Python 要發 HTTP 請求時,都直覺地 import requestsrequests 確實是經典中的經典,但它誕生於 2011 年,在這個非同步當道、HTTP/2 普及的年代,有沒有更現代的選擇呢?

答案就是 httpx

httpx 的 API 幾乎和 requests 一模一樣(無痛轉換!),卻多了這些超強功能:

  • ✅ 完整支援 async/await 非同步請求
  • ✅ 原生支援 HTTP/2
  • ✅ 內建 串流下載串流上傳
  • ✅ 支援 timeout 細粒度控制
  • ✅ 100% type-annotated,IDE 提示超友善

今天拍拍君就帶你從零開始,一步步掌握 httpx 的所有必備技巧!


二. 安裝
#

使用 pip 安裝:

pip install httpx

如果你用 uv(拍拍君大推!):

uv add httpx

要啟用 HTTP/2 支援,需要額外安裝:

pip install httpx[http2]

確認安裝成功:

import httpx
print(httpx.__version__)
# 0.28.1

三. 基本用法:和 requests 幾乎一樣
#

如果你熟悉 requests,那 httpx 上手零門檻!

GET 請求
#

import httpx

response = httpx.get("https://httpbin.org/get")
print(response.status_code)  # 200
print(response.json())       # 回傳 JSON dict

POST 請求
#

import httpx

data = {"name": "拍拍君", "skill": "Python"}
response = httpx.post("https://httpbin.org/post", json=data)
print(response.json()["json"])
# {'name': '拍拍君', 'skill': 'Python'}

帶查詢參數
#

import httpx

params = {"q": "python httpx", "page": 1}
response = httpx.get("https://httpbin.org/get", params=params)
print(response.url)
# https://httpbin.org/get?q=python+httpx&page=1

自訂 Headers
#

import httpx

headers = {"Authorization": "Bearer my-secret-token"}
response = httpx.get("https://httpbin.org/headers", headers=headers)
print(response.json()["headers"]["Authorization"])
# Bearer my-secret-token

是不是跟 requests 完全一樣的感覺?沒錯,這就是 httpx 的設計理念 — 零學習成本轉換


四. Client 物件:連線效能大提升
#

每次用 httpx.get() 都會建立新連線。如果你要連續發很多請求,用 Client 可以重用 TCP 連線,速度飛快!

基本用法
#

import httpx

with httpx.Client() as client:
    # 這三個請求共用同一條 TCP 連線
    r1 = client.get("https://httpbin.org/get")
    r2 = client.get("https://httpbin.org/ip")
    r3 = client.get("https://httpbin.org/user-agent")

print(r1.status_code, r2.status_code, r3.status_code)
# 200 200 200

設定預設值
#

Client 可以設定全域的 headers、base_url、timeout 等等:

import httpx

with httpx.Client(
    base_url="https://api.github.com",
    headers={"Accept": "application/vnd.github.v3+json"},
    timeout=10.0,
) as client:
    # 自動加上 base_url 和 headers
    repos = client.get("/users/python/repos")
    print(f"Python 有 {len(repos.json())} 個公開 repo")

這樣寫 API 客戶端超乾淨!


五. Async 非同步請求:httpx 的殺手鐧
#

這是 httpx 相比 requests 最大的優勢 — 原生支援 async/await

基本非同步請求
#

import httpx
import asyncio

async def fetch_data():
    async with httpx.AsyncClient() as client:
        response = await client.get("https://httpbin.org/get")
        return response.json()

result = asyncio.run(fetch_data())
print(result["url"])
# https://httpbin.org/get

並行請求:速度起飛 🚀
#

同時發出多個請求,不用等前一個完成:

import httpx
import asyncio
import time

urls = [
    "https://httpbin.org/delay/2",
    "https://httpbin.org/delay/2",
    "https://httpbin.org/delay/2",
    "https://httpbin.org/delay/2",
    "https://httpbin.org/delay/2",
]

async def fetch_all():
    async with httpx.AsyncClient() as client:
        tasks = [client.get(url) for url in urls]
        responses = await asyncio.gather(*tasks)
        return [r.status_code for r in responses]

start = time.time()
results = asyncio.run(fetch_all())
elapsed = time.time() - start

print(f"狀態碼: {results}")
print(f"耗時: {elapsed:.1f} 秒")
# 同步要 10 秒,非同步只要約 2 秒!

五個各要 2 秒的請求,同步得花 10 秒,但非同步並行只要約 2 秒。這就是非同步的威力!

搭配 asyncio.as_completed
#

如果你想先到先處理

import httpx
import asyncio

async def fetch_and_print():
    async with httpx.AsyncClient() as client:
        tasks = {
            asyncio.create_task(client.get(f"https://httpbin.org/delay/{i}")): i
            for i in [3, 1, 2]
        }
        for coro in asyncio.as_completed(tasks.keys()):
            response = await coro
            data = response.json()
            print(f"完成: delay={data['url'].split('/')[-1]}s")

asyncio.run(fetch_and_print())
# 完成: delay=1s
# 完成: delay=2s
# 完成: delay=3s

六. Timeout 精細控制
#

requests 只能設一個 timeout 數字,httpx 可以分別控制不同階段:

import httpx

# 簡單用法(和 requests 一樣)
response = httpx.get("https://httpbin.org/get", timeout=5.0)

# 進階用法:分別控制各階段 timeout
timeout = httpx.Timeout(
    connect=5.0,    # 建立連線最多 5 秒
    read=10.0,      # 讀取回應最多 10 秒
    write=5.0,      # 發送請求最多 5 秒
    pool=5.0,       # 等待連線池最多 5 秒
)

with httpx.Client(timeout=timeout) as client:
    response = client.get("https://httpbin.org/delay/3")
    print(response.status_code)  # 200

處理 Timeout 例外
#

import httpx

try:
    response = httpx.get("https://httpbin.org/delay/5", timeout=2.0)
except httpx.TimeoutException:
    print("請求超時了!拍拍君等不及啦 😤")
except httpx.HTTPError as e:
    print(f"HTTP 錯誤: {e}")

七. HTTP/2 支援
#

HTTP/2 可以在同一條 TCP 連線上多工傳輸,對 API 密集應用很有幫助:

import httpx

with httpx.Client(http2=True) as client:
    response = client.get("https://www.google.com")
    print(response.http_version)
    # HTTP/2

💡 記得先安裝 httpx[http2] 才能啟用喔!


八. 串流下載:處理大檔案
#

下載大檔案時,不想把整個檔案載入記憶體?用串流!

import httpx

url = "https://speed.hetzner.de/100MB.bin"

with httpx.stream("GET", url) as response:
    total = int(response.headers.get("content-length", 0))
    downloaded = 0

    with open("big_file.bin", "wb") as f:
        for chunk in response.iter_bytes(chunk_size=8192):
            f.write(chunk)
            downloaded += len(chunk)
            pct = (downloaded / total * 100) if total else 0
            print(f"\r下載中... {pct:.1f}%", end="", flush=True)

print("\n下載完成!🎉")

非同步版本:

import httpx
import asyncio

async def download_large_file():
    async with httpx.AsyncClient() as client:
        async with client.stream("GET", "https://speed.hetzner.de/100MB.bin") as response:
            with open("big_file.bin", "wb") as f:
                async for chunk in response.aiter_bytes(chunk_size=8192):
                    f.write(chunk)
    print("非同步下載完成!🚀")

asyncio.run(download_large_file())

九. 實戰範例:打造簡易 API 客戶端
#

來整合前面學的,做一個 GitHub API 客戶端:

import httpx
import asyncio
from dataclasses import dataclass

@dataclass
class GitHubRepo:
    name: str
    stars: int
    language: str | None
    url: str

class GitHubClient:
    def __init__(self, token: str | None = None):
        headers = {"Accept": "application/vnd.github.v3+json"}
        if token:
            headers["Authorization"] = f"Bearer {token}"
        self._client = httpx.AsyncClient(
            base_url="https://api.github.com",
            headers=headers,
            timeout=httpx.Timeout(connect=5.0, read=15.0, write=5.0, pool=5.0),
        )

    async def __aenter__(self):
        return self

    async def __aexit__(self, *args):
        await self._client.aclose()

    async def get_user_repos(self, username: str) -> list[GitHubRepo]:
        response = await self._client.get(
            f"/users/{username}/repos",
            params={"sort": "stars", "per_page": 5},
        )
        response.raise_for_status()
        return [
            GitHubRepo(
                name=repo["name"],
                stars=repo["stargazers_count"],
                language=repo["language"],
                url=repo["html_url"],
            )
            for repo in response.json()
        ]

    async def get_repo_info(self, owner: str, repo: str) -> dict:
        response = await self._client.get(f"/repos/{owner}/{repo}")
        response.raise_for_status()
        return response.json()

async def main():
    async with GitHubClient() as gh:
        # 取得使用者的熱門 repo
        repos = await gh.get_user_repos("python")
        print("🐍 Python 組織的熱門 Repo:")
        for repo in repos:
            lang = repo.language or "N/A"
            print(f"  ⭐ {repo.stars:>6,} | {repo.name} ({lang})")

        # 查詢特定 repo
        info = await gh.get_repo_info("encode", "httpx")
        print(f"\n📦 httpx: ⭐ {info['stargazers_count']:,} stars")

asyncio.run(main())

執行結果(數字會隨時間變化):

🐍 Python 組織的熱門 Repo:
  ⭐ 65,432 | cpython (C)
  ⭐ 12,345 | mypy (Python)
  ⭐  8,765 | typeshed (Python)
  ⭐  5,432 | peps (reStructuredText)
  ⭐  3,210 | devguide (reStructuredText)

📦 httpx: ⭐ 14,567 stars

結語
#

httpx 可以說是 requests 的完美升級版

功能 requests httpx
同步請求
非同步請求
HTTP/2
Timeout 細粒度控制
Type hints 部分 完整
API 相容性 幾乎相同

如果你正在開始新專案,拍拍君強烈建議直接用 httpx!尤其是需要非同步的場景(Web 框架、爬蟲、批量 API 呼叫),httpx 會讓你的程式碼又快又優雅。

下次寫程式要發 HTTP 請求時,試試把 import requests 換成 import httpx 吧!你會愛上它的 ❤️


延伸閱讀
#

Python 學習 - 本文屬於一個選集。
§ 15: 本文

相關文章

超快速 Python 套件管理:uv 完全教學
·6 分鐘· loading · loading
Python Uv Package Manager Rust
Python 資料驗證小幫手:Pydantic
·4 分鐘· loading · loading
Python Pydantic Data Validation
Python Typing:讓你的程式碼更安全、更好維護
·4 分鐘· loading · loading
Python Typing Type Hints
科學計算:數值積分
·5 分鐘· loading · loading
Python Numpy Scipy Numerical Methods Numerical Integral
讓你的終端機華麗變身:Rich 套件教學
·2 分鐘· loading · loading
Python Rich Cli
Python: 我需要進度條! tqdm
·3 分鐘· loading · loading
Python Tqdm Data Science