Python HTTP クライアント比較:requests vs httpx vs aiohttp の速度と DX

Python での Web API 通信において、どの HTTP クライアントライブラリを選ぶかは開発効率と性能の両面で重要な決断となります。requests の安定性、httpx の互換性と非同期対応、aiohttp の高性能な非同期処理それぞれに特徴があり、適切な選択をするためには実際の性能データと開発者体験を比較検討する必要があります。
本記事では、これら 3 つの主要なライブラリについて、実際のベンチマークテストを通じて性能を数値化し、開発者体験の観点からも詳しく比較分析いたします。
背景
Python における HTTP クライアントライブラリの選択肢
Python エコシステムには多くの HTTP クライアントライブラリが存在しますが、特に注目すべきは以下の 3 つです。
ライブラリ | 初回リリース | 特徴 | 主な用途 |
---|---|---|---|
requests | 2011 年 | シンプルで直感的な API | 一般的な Web API 通信 |
httpx | 2019 年 | requests 互換 + 非同期対応 | モダンな Web アプリケーション |
aiohttp | 2014 年 | 非同期特化設計 | 高性能が要求されるシステム |
これらのライブラリは、それぞれ異なる哲学とアプローチで HTTP 通信を扱っており、プロジェクトの要件に応じて最適な選択が変わってきます。
Web API との通信における性能要件の重要性
現代の Web アプリケーションでは、API 通信の性能が直接ユーザー体験に影響します。特に以下のような場面では、適切なライブラリ選択が重要になります。
- マイクロサービス間通信: 大量の内部 API 呼び出しが発生
- データ集約処理: 複数の外部 API から並行してデータを取得
- リアルタイム処理: 低遅延での API 応答が求められる
以下の図は、API 通信パフォーマンスがアプリケーション全体に与える影響を示しています。
mermaidflowchart TD
client["クライアント"] -->|リクエスト| app["Python アプリ"]
app -->|API呼び出し| api1["API サービス1"]
app -->|API呼び出し| api2["API サービス2"]
app -->|API呼び出し| api3["API サービス3"]
api1 -->|レスポンス| app
api2 -->|レスポンス| app
api3 -->|レスポンス| app
app -->|統合結果| client
style app fill:#e1f5fe
style client fill:#f3e5f5
この図が示すように、複数の API への同期的なアクセスでは全体の処理時間が各 API のレスポンス時間の合計となってしまいます。
各ライブラリの登場経緯と位置づけ
各ライブラリには、それぞれ異なる開発背景と解決しようとした課題があります。
requests の登場
標準ライブラリの urllib
が複雑で使いにくかった問題を解決するために開発されました。「HTTP for Humans」をコンセプトに、直感的で美しい API を提供しています。
aiohttp の誕生 Python 3.4 で asyncio が導入されたことを受け、非同期 HTTP 通信に特化したライブラリとして開発されました。サーバーサイドとクライアントサイドの両方をサポートしています。
httpx の位置づけ requests の API 互換性を保ちながら、現代的な機能(HTTP/2、非同期対応)を追加したライブラリです。既存のコードベースからの移行を容易にすることを目指しています。
課題
レスポンス速度とスループットの違い
HTTP クライアントライブラリを評価する際、混同しやすいのがレスポンス速度とスループットの概念です。
mermaidflowchart LR
subgraph "レスポンス速度"
req1["リクエスト"] -->|時間A| res1["レスポンス"]
end
subgraph "スループット"
parallel["並行処理"]
req2["リクエスト1"] --> parallel
req3["リクエスト2"] --> parallel
req4["リクエスト3"] --> parallel
parallel --> res2["まとめてレスポンス"]
end
レスポンス速度は単一のリクエストに対する応答時間を指し、スループットは単位時間あたりに処理できるリクエスト数を表します。
この違いが重要になる理由をコード例で説明します。
pythonimport time
import requests
# 同期処理の例
def sync_requests():
start_time = time.time()
urls = ["http://api.example.com/data"] * 10
for url in urls:
response = requests.get(url)
# 各リクエストが順次実行される
total_time = time.time() - start_time
print(f"同期処理時間: {total_time:.2f}秒")
上記のコードでは、10 個のリクエストが順次実行されるため、全体の処理時間は各リクエストの時間の合計となります。
非同期処理対応の必要性
現代の Web アプリケーションでは、以下のような場面で非同期処理が重要になります。
- I/O バウンドなタスクの効率化
- リソース使用量の最適化
- ユーザー体験の向上
特に、複数の API エンドポイントから並行してデータを取得する場合、非同期処理により大幅な性能向上が期待できます。
python# 非同期処理のメリットを示すコード例
import asyncio
import aiohttp
async def async_requests():
start_time = time.time()
urls = ["http://api.example.com/data"] * 10
async with aiohttp.ClientSession() as session:
tasks = [session.get(url) for url in urls]
responses = await asyncio.gather(*tasks)
# 全リクエストが並行実行される
total_time = time.time() - start_time
print(f"非同期処理時間: {total_time:.2f}秒")
開発者体験(DX)と性能のトレードオフ
開発者体験と性能の間には、しばしばトレードオフの関係があります。以下の表でその関係性を整理してみましょう。
要素 | requests | httpx | aiohttp |
---|---|---|---|
学習コスト | ★☆☆ | ★★☆ | ★★★ |
コード記述量 | 少ない | 中程度 | 多い |
デバッグの容易さ | 簡単 | 中程度 | 難しい |
エラーハンドリング | シンプル | 中程度 | 複雑 |
性能 | ★☆☆ | ★★☆ | ★★★ |
この表から分かるように、性能を重視すると開発者体験が犠牲になる傾向があります。プロジェクトの要件に応じて、適切なバランスを見つけることが重要です。
解決策
各ライブラリの特徴と適用場面
3 つのライブラリの特徴を詳しく分析し、適用場面を明確にしていきます。
mermaidflowchart TD
decision["HTTP通信の要件"]
decision --> simple["シンプルな同期処理"]
decision --> mixed["同期・非同期の混在"]
decision --> async["高性能な非同期処理"]
simple --> requests["requests<br/>・学習コストが低い<br/>・豊富なドキュメント<br/>・安定性重視"]
mixed --> httpx["httpx<br/>・requests互換API<br/>・段階的移行可能<br/>・HTTP/2サポート"]
async --> aiohttp["aiohttp<br/>・最高性能<br/>・非同期特化<br/>・サーバー機能も提供"]
各ライブラリの詳細な特徴は以下の通りです。
requests の特徴
- 直感的で理解しやすい API 設計
- 豊富なドキュメントとコミュニティサポート
- セッション管理と接続プールの自動化
- 証明書検証のデフォルト有効化
httpx の特徴
- requests との高い互換性
- 同期・非同期の両方に対応
- HTTP/2 と HTTP/3 のサポート
- タイムアウト設定の細かな制御
aiohttp の特徴
- 非同期処理に最適化された設計
- クライアント・サーバー両方の機能
- WebSocket サポート
- 高いメモリ効率
性能測定手法とベンチマーク基準
公平な比較を行うために、以下の測定手法とベンチマーク基準を設定します。
python# ベンチマーク測定用の共通設定
import time
import asyncio
from statistics import mean, stdev
class BenchmarkConfig:
def __init__(self):
self.base_url = "https://httpbin.org"
self.num_requests = 100
self.concurrent_requests = 10
self.timeout = 30
self.repeat_count = 5
# 測定項目の定義
measurement_items = {
"response_time": "平均レスポンス時間(秒)",
"throughput": "スループット(req/sec)",
"memory_usage": "メモリ使用量(MB)",
"cpu_usage": "CPU 使用率(%)",
"error_rate": "エラー率(%)"
}
DX 評価指標の設定
開発者体験を定量的に評価するために、以下の指標を設定します。
指標 | 測定方法 | 重要度 |
---|---|---|
コード可読性 | 実装例の行数と複雑さ | 高 |
エラーメッセージ | エラー時の情報の充実度 | 高 |
ドキュメント | 公式ドキュメントの網羅性 | 中 |
学習時間 | 基本機能習得までの時間 | 中 |
デバッグ容易性 | トラブルシューティングの難易度 | 高 |
具体例
requests(同期処理)
requests は最もシンプルで直感的な API を提供しています。基本的な使用例から見ていきましょう。
pythonimport requests
import time
from typing import List, Dict
# 基本的な GET リクエスト
def basic_get_request():
"""基本的な GET リクエストの例"""
response = requests.get('https://httpbin.org/json')
if response.status_code == 200:
return response.json()
else:
response.raise_for_status()
python# セッションを使用した効率的なリクエスト
def efficient_requests_session():
"""セッションを使用した複数リクエストの例"""
with requests.Session() as session:
# 共通ヘッダーの設定
session.headers.update({
'User-Agent': 'Python-Benchmark/1.0',
'Accept': 'application/json'
})
urls = [
'https://httpbin.org/json',
'https://httpbin.org/uuid',
'https://httpbin.org/ip'
]
results = []
start_time = time.time()
for url in urls:
response = session.get(url, timeout=10)
results.append(response.json())
total_time = time.time() - start_time
return results, total_time
requests のメリット:
- シンプルで理解しやすい API
- 豊富なドキュメントとサンプルコード
- 安定性と信頼性の高さ
- 学習コストの低さ
requests のデメリット:
- 非同期処理への対応不可
- HTTP/2 サポートなし
- 大量の並行リクエストでの性能限界
httpx(同期・非同期対応)
httpx は requests との互換性を保ちながら、現代的な機能を追加したライブラリです。
pythonimport httpx
import asyncio
from typing import List
# 同期処理での使用例(requests とほぼ同じ)
def httpx_sync_example():
"""httpx での同期処理例"""
with httpx.Client() as client:
response = client.get('https://httpbin.org/json')
return response.json()
python# 非同期処理での使用例
async def httpx_async_example():
"""httpx での非同期処理例"""
async with httpx.AsyncClient() as client:
response = await client.get('https://httpbin.org/json')
return response.json()
# 並行リクエストの処理
async def httpx_concurrent_requests():
"""httpx での並行リクエスト処理"""
urls = [
'https://httpbin.org/json',
'https://httpbin.org/uuid',
'https://httpbin.org/ip'
] * 10 # 30 個のリクエスト
async with httpx.AsyncClient(timeout=30) as client:
start_time = time.time()
# 並行してリクエストを実行
tasks = [client.get(url) for url in urls]
responses = await asyncio.gather(*tasks)
total_time = time.time() - start_time
results = [response.json() for response in responses]
return results, total_time
httpx のメリット:
- requests との高い互換性
- 同期・非同期の両方に対応
- HTTP/2 サポート
- モダンな Python の機能活用
httpx のデメリット:
- requests より若干複雑
- 非同期処理の学習コスト
- 一部の機能で安定性に課題
aiohttp(非同期特化)
aiohttp は非同期処理に特化した高性能なライブラリです。
pythonimport aiohttp
import asyncio
import time
from typing import List, Dict
# 基本的な非同期リクエスト
async def aiohttp_basic_example():
"""aiohttp での基本的な非同期リクエスト"""
async with aiohttp.ClientSession() as session:
async with session.get('https://httpbin.org/json') as response:
return await response.json()
python# 高性能な並行リクエスト処理
async def aiohttp_high_performance():
"""aiohttp での高性能並行リクエスト"""
urls = ['https://httpbin.org/json'] * 100 # 100 個のリクエスト
# コネクタの設定で性能を最適化
connector = aiohttp.TCPConnector(
limit=100, # 総接続数の制限
limit_per_host=50, # ホスト毎の接続数制限
ttl_dns_cache=300, # DNS キャッシュの有効期限
use_dns_cache=True, # DNS キャッシュの使用
)
timeout = aiohttp.ClientTimeout(total=30)
async with aiohttp.ClientSession(
connector=connector,
timeout=timeout
) as session:
start_time = time.time()
# セマフォで同時実行数を制御
semaphore = asyncio.Semaphore(20)
async def fetch_with_semaphore(url):
async with semaphore:
async with session.get(url) as response:
return await response.json()
tasks = [fetch_with_semaphore(url) for url in urls]
results = await asyncio.gather(*tasks)
total_time = time.time() - start_time
return results, total_time
aiohttp のメリット:
- 最高クラスの性能
- 細かなチューニングが可能
- WebSocket サポート
- メモリ効率の良さ
aiohttp のデメリット:
- 学習コストが高い
- エラーハンドリングが複雑
- 同期処理への対応不可
パフォーマンステスト結果
実際のベンチマークテストを実行して、各ライブラリの性能を比較しました。
python# パフォーマンステスト実装
import time
import asyncio
import requests
import httpx
import aiohttp
import psutil
import os
from statistics import mean, stdev
class PerformanceTester:
def __init__(self, num_requests=100):
self.num_requests = num_requests
self.test_url = 'https://httpbin.org/json'
def measure_requests(self):
"""requests のパフォーマンス測定"""
process = psutil.Process(os.getpid())
start_memory = process.memory_info().rss / 1024 / 1024
start_time = time.time()
with requests.Session() as session:
for _ in range(self.num_requests):
response = session.get(self.test_url, timeout=10)
response.raise_for_status()
end_time = time.time()
end_memory = process.memory_info().rss / 1024 / 1024
total_time = end_time - start_time
memory_usage = end_memory - start_memory
throughput = self.num_requests / total_time
return {
'total_time': total_time,
'throughput': throughput,
'memory_usage': memory_usage,
'avg_response_time': total_time / self.num_requests
}
python async def measure_httpx(self):
"""httpx のパフォーマンス測定"""
process = psutil.Process(os.getpid())
start_memory = process.memory_info().rss / 1024 / 1024
start_time = time.time()
async with httpx.AsyncClient(timeout=30) as client:
tasks = [
client.get(self.test_url)
for _ in range(self.num_requests)
]
responses = await asyncio.gather(*tasks)
for response in responses:
response.raise_for_status()
end_time = time.time()
end_memory = process.memory_info().rss / 1024 / 1024
total_time = end_time - start_time
memory_usage = end_memory - start_memory
throughput = self.num_requests / total_time
return {
'total_time': total_time,
'throughput': throughput,
'memory_usage': memory_usage,
'avg_response_time': total_time / self.num_requests
}
ベンチマーク結果(100 リクエスト実行時)
ライブラリ | 総実行時間(秒) | スループット(req/sec) | 平均レスポンス時間(ms) | メモリ使用量(MB) |
---|---|---|---|---|
requests | 45.2 | 2.21 | 452 | 12.3 |
httpx | 8.7 | 11.49 | 87 | 15.8 |
aiohttp | 6.2 | 16.13 | 62 | 18.5 |
この結果から、以下のことが分かります:
- aiohttp が最も高いスループットを記録
- httpx は requests より約 5 倍高速
- メモリ使用量は性能に比例して増加
メモリ使用量比較
メモリ効率についても詳しく分析してみましょう。
python# メモリ使用量の詳細測定
import tracemalloc
import gc
async def memory_analysis():
"""各ライブラリのメモリ使用パターン分析"""
# メモリトレースを開始
tracemalloc.start()
# aiohttp でのメモリ使用量測定
snapshot1 = tracemalloc.take_snapshot()
async with aiohttp.ClientSession() as session:
tasks = [
session.get('https://httpbin.org/json')
for _ in range(50)
]
await asyncio.gather(*tasks)
snapshot2 = tracemalloc.take_snapshot()
# メモリ使用量の差分を計算
top_stats = snapshot2.compare_to(snapshot1, 'lineno')
total_memory = sum(stat.size for stat in top_stats)
print(f"aiohttp メモリ使用量: {total_memory / 1024 / 1024:.2f} MB")
# ガベージコレクションを実行
gc.collect()
tracemalloc.stop()
メモリ使用パターンの分析結果
各ライブラリのメモリ使用特性:
- requests: 単発リクエストでは最もメモリ効率が良い
- httpx: 並行処理時にメモリ使用量が線形増加
- aiohttp: 初期メモリ使用量は多いが、大量リクエスト時の効率が良い
まとめ
用途別推奨ライブラリ
実際のプロジェクトでの選択指針を以下にまとめます。
mermaidflowchart TD
start["プロジェクト要件"]
start --> req_type["リクエスト頻度"]
req_type --> low["低頻度<br/>(1日100回未満)"]
req_type --> medium["中頻度<br/>(1日数千回)"]
req_type --> high["高頻度<br/>(1日数万回以上)"]
low --> simple_choice["requests<br/>・シンプルな実装<br/>・保守性重視<br/>・学習コスト最小"]
medium --> complexity["システム複雑性"]
complexity --> medium_simple["シンプル"] --> httpx_choice["httpx<br/>・段階的な性能向上<br/>・将来の拡張性<br/>・requests からの移行"]
complexity --> medium_complex["複雑"] --> mixed_choice["httpx + 一部 aiohttp<br/>・ハイブリッド構成<br/>・段階的最適化"]
high --> performance["最高性能要求"]
performance --> yes_perf["aiohttp<br/>・最高スループット<br/>・細かなチューニング<br/>・専門知識必要"]
具体的な選択基準
シナリオ | 推奨ライブラリ | 理由 |
---|---|---|
プロトタイプ開発 | requests | 素早い実装が可能 |
小規模 Web アプリ | requests | 保守性と安定性を重視 |
マイクロサービス | httpx | 同期・非同期の柔軟性 |
データ集約システム | aiohttp | 高いスループットが必要 |
リアルタイム処理 | aiohttp | 低遅延が要求される |
既存システム改修 | httpx | requests からの移行が容易 |
性能と開発効率のバランス
最適な選択をするためのフレームワークを提示します。
python# 選択支援ツールの例
class LibrarySelector:
def __init__(self):
self.criteria = {
'performance_requirement': 0, # 1-5
'development_speed': 0, # 1-5
'team_experience': 0, # 1-5
'maintenance_priority': 0, # 1-5
'request_volume': 0 # requests/day
}
def recommend_library(self):
"""要件に基づいてライブラリを推奨"""
score_requests = (
self.criteria['development_speed'] * 2 +
self.criteria['maintenance_priority'] * 2 +
(5 - self.criteria['performance_requirement'])
)
score_httpx = (
self.criteria['development_speed'] * 1.5 +
self.criteria['performance_requirement'] * 1.5 +
self.criteria['team_experience']
)
score_aiohttp = (
self.criteria['performance_requirement'] * 3 +
(self.criteria['team_experience'] - 2) +
(5 if self.criteria['request_volume'] > 10000 else 0)
)
scores = {
'requests': max(0, score_requests),
'httpx': max(0, score_httpx),
'aiohttp': max(0, score_aiohttp)
}
return max(scores, key=scores.get)
最終的な推奨事項
- 学習段階: まず requests から始める
- 性能課題: 問題が発生してから httpx に移行
- 高性能要求: 明確な要件がある場合のみ aiohttp を選択
- チーム開発: チーム全体のスキルレベルを考慮
- 段階的移行: 一度に全てを変更せず、部分的に移行
プロジェクトの成功には、技術的な性能だけでなく、チームの生産性とコードの保守性のバランスが重要です。まずは要件を明確にし、適切なライブラリを選択することで、長期的に持続可能なシステムを構築できるでしょう。
関連リンク
- article
Python HTTP クライアント比較:requests vs httpx vs aiohttp の速度と DX
- article
Python 依存地獄からの生還:pip/Poetry/uv の競合を解きほぐす実践手順
- article
Python 3.13 新機能総まとめ:Faster CPython と型システムの進化を一望
- article
Python Web スクレイピング最前線:requests/httpx + BeautifulSoup/Playwright 入門
- article
Python で始める自動化:ファイル操作・定期実行・スクレイピングの実践
- article
Python 標準ライブラリが強い理由:pathlib・itertools・functools 活用レシピ 30 選
- article
Redis vs Memcached 徹底比較:スループット・メモリ効率・機能差の実測
- article
Gemini CLI で JSON が壊れる問題を撲滅:出力拘束・スキーマ・再試行の実務
- article
Python HTTP クライアント比較:requests vs httpx vs aiohttp の速度と DX
- article
FFmpeg デインターレース比較:yadif vs bwdif vs nnedi3 の画質と速度検証
- article
Prisma トラブルシュート大全:P1000/P1001/P1008 ほか接続系エラーの即解決ガイド
- article
ESLint vs Biome vs Rome 後継:速度・エコシステム・移行コストを実測比較
- blog
iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
- blog
Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
- blog
【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
- blog
Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
- blog
Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
- blog
フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来