Python httpx 完整指南:高效的 HTTP 客戶端

更新日期: 2025 年 3 月 3 日

在 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() 類似,但 httpxClient 支援更多功能,例如 連線池(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 回應

在這段程式碼中:

  1. 建立 httpx.Client() 物件
    • client = httpx.Client() 會創建一個 HTTP 會話,在這個會話內所有請求都會共享同一個 TCP 連線
  2. 發送 GET 請求
    • client.get("https://jsonplaceholder.typicode.com/posts/1") 會向 API 伺服器發送請求,並取得回應。
  3. 自動關閉會話
    • with httpx.Client() as client: 會確保程式執行完畢後,自動關閉會話並釋放資源。

什麼是 TCP 連線?

TCP(Transmission Control Protocol,傳輸控制協議)網路通訊的基礎協議之一,它確保資料在網路上能夠可靠地傳輸。

當你的電腦要與伺服器溝通時(例如瀏覽網站、發送 API 請求),它會透過 TCP 連線 來建立穩定的數據傳輸管道,確保資料完整、順序正確地抵達目的地。

舉個例子,你打開瀏覽器訪問 https://example.com,你的電腦會:

  1. 建立一條 TCP 連線:你的電腦與 example.com 伺服器之間建立一條「專屬的溝通管道」。
  2. 發送 HTTP 請求:你的電腦通過這條 TCP 連線發送請求給 example.com
  3. 接收 HTTP 回應example.com 伺服器回應你的請求,將網頁數據傳回來。
  4. 關閉 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())

這段程式碼的運作方式:

  1. httpx.Client() 建立一個 HTTP 會話,在 with 區塊內,client 物件會管理 HTTP 連線。
  2. 第一個請求(client.get())建立了一條 TCP 連線,並且 保持開啟
  3. 第二個、第三個請求重用同一條 TCP 連線,不會重新建立新的連線。
  4. 程式離開 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")

這樣的問題是:

  1. 每次請求都要重新建立 TCP 連線,浪費系統資源。
  2. 在高併發場景下(如爬蟲、大量 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())

在這段程式碼中:

  1. 使用 async with httpx.AsyncClient()
    • AsyncClienthttpx異步版本,可以讓我們用 await 來發送請求,而不會阻塞主程式。
  2. 透過 await client.get() 來發送請求
    • await 允許程式在等待伺服器回應時,不會卡住 CPU,而是繼續執行其他任務。
  3. 使用 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())
執行步驟解析
  1. 建立 fetch(url) 函式來發送單個異步請求
    • async with httpx.AsyncClient() 創建一個異步 HTTP 客戶端。
    • await client.get(url) 會發送 HTTP GET 請求,但不會阻塞主執行緒。
    • return response.json() 會將 API 回應解析成 Python 字典並回傳。
  2. main() 函式中:
    • 建立多個 URL 清單。
    • 使用 列表推導式(List Comprehension) 建立 多個 fetch() 任務
    • asyncio.gather(*tasks) 並行執行所有請求,等待它們全部完成後回傳結果。
  3. 透過 asyncio.run(main()) 執行所有請求
    • asyncio.run() 會執行 main(),確保所有請求同時發送,而不是一個一個等待回應後再發送下一個請求。

為什麼要使用 asyncio.gather()

並行執行多個請求,大幅提升速度。
減少 API 等待時間,因為所有請求是 同時發送 而不是 依序發送
適合爬蟲、高頻 API 請求等應用


httpx vs requests:該選哪一個?

特性httpxrequests
同步請求✅ 支援✅ 支援
異步請求✅ 支援❌ 不支援
HTTP/2✅ 支援❌ 不支援
內建連線池✅ 有❌ 無
擴展性✅ 強✅ 強

如果你的應用只需要同步請求,requests 依然是好選擇;但如果你需要 異步、HTTP/2、連線池httpx 會是更好的選擇!


結論

  • httpxrequests 的強化版,支援 同步 & 異步請求,適用於一般與高併發應用。
  • 內建 HTTP/2、連線池、測試工具,適合用於 API 開發、爬蟲等應用場景。
  • 若只需同步請求,可繼續使用 requests;但如果需要異步或高效能,建議使用 httpx! 🚀

希望這篇文章能幫助你快速掌握 httpx,讓你的 Python 開發更高效!

Similar Posts