閉じる

【Python】requests.Sessionで接続を再利用してAPIを高速化する方法

Web APIを何度も呼ぶと、毎回の接続確立やTLSハンドシェイクが積み上がり、思った以上に遅くなります。

requests.Sessionを使えば接続(Keep-Alive)やヘッダー、クッキーを再利用でき、通信の無駄を減らせます。

この記事では基本から実践まで丁寧に解説します。

requests.Sessionとは?接続再利用でAPIを高速化

同じホストへ繰り返しアクセスするならrequests.Sessionを使うだけで速く、安定した通信が期待できます

内部で接続をプーリングし、HTTPのKeep-Aliveを活用して無駄なハンドシェイクを減らすためです。

初心者の方でも、まずは「毎回のrequests.getではなく、1つのセッションでまとめる」と覚えておくと良いです。

セッションが速い理由(Keep-Alive)

HTTP/1.1では持続的接続(Keep-Alive)が標準で有効です。

requests.Sessionを使うと、内部でコネクションプールが作られ、同じホストへの後続リクエストは既存のTCP接続を再利用します。

これにより以下のコストが減ります。

  • TCPハンドシェイク(3-way handshake)
  • TLSハンドシェイク(HTTPSの証明書検証など)
  • 接続確立待ち時間

接続再利用は「1回あたりの数十ミリ秒〜数百ミリ秒」を積み重ねて短縮します

大量のAPI呼び出しを行う場面で特に効果が大きくなります。

単発requests.getとの違い

requests.get(...)を直接呼ぶたびに内部ではSessionが都度作られ、利用後に破棄されます。

そのため、呼び出しごとに新しい接続を張り直す可能性が高くなります。

一方で、requests.Sessionを明示的に使うと、接続・クッキー・ヘッダーなどが継続して利用されます。

以下に違いを簡単にまとめます。

比較項目単発のrequests.getrequests.Session
接続確立毎回やり直す傾向再利用(プール)
パフォーマンス遅くなりやすい速くなりやすい
Cookie保持呼び出し間で失われる自動で保持
共通ヘッダー毎回指定が必要1回設定で使い回し
ベストプラクティス周回処理では非推奨周回処理で推奨

初心者が押さえるメリット

速い、書きやすい、安定しやすいの3点が要点です。

具体的には、共通ヘッダーや認証トークンを一度設定して使い回せるため、コードが読みやすくなり、また接続再利用で高速化されます。

Cookieを跨いで維持できるため、ログイン後の連続アクセスなども簡単です。

基本の使い方

ここでは最小限のパターンから段階的に使い方を解説します。

まずは1つのSessionを作り、使い終わったら閉じる、という流れを習得しましょう。

Sessionを作成する

requests.Session()でセッションを作成し、同じオブジェクトで複数回APIを呼びます。

Python
# 基本: Sessionを作ってGETする
import requests

# セッションを作成
s = requests.Session()

# サンプルAPIにアクセス
resp = s.get("http://httpbin.org/get", timeout=10)

# ステータスや一部ヘッダーを確認
print("status:", resp.status_code)
print("connection:", resp.headers.get("Connection"))

# 使い終わったら明示的にクローズ
s.close()
実行結果
status: 200
connection: keep-alive

with文で自動でcloseする

Pythonのコンテキストマネージャを使えば、ブロックを抜けたときに自動でクローズされます。

これが最も安全で簡単なパターンです。

Python
# with文で使うと自動でcloseされます
import requests

url = "http://httpbin.org/get"

with requests.Session() as s:
    r = s.get(url, timeout=10)
    print("ok?", r.ok)
    # withブロックを出ると自動でs.close()が呼ばれる
実行結果
ok? True

GETやPOSTの呼び出し例(API)

同じセッションでGETとPOSTを続けて呼ぶ例です。

クッキーや接続が自動で引き継がれます。

Python
# GETとPOSTを同じセッションで呼ぶ例
import requests

with requests.Session() as s:
    # GET: クエリ文字列を付与
    r1 = s.get("https://httpbin.org/get", params={"query": "apple"}, timeout=10)
    data1 = r1.json()
    print("GET args:", data1["args"])

    # POST: JSONボディを送信
    r2 = s.post("https://httpbin.org/post", json={"name": "Alice"}, timeout=10)
    data2 = r2.json()
    print("POST JSON:", data2["json"])
実行結果
GET args: {'query': 'apple'}
POST JSON: {'name': 'Alice'}

共通ヘッダーを一度だけ設定する

s.headers.update(...)でセッション共通のヘッダーを付与できます。

毎回同じヘッダーを書く必要がなくなり、ミスの防止にもつながります。

Python
# 共通ヘッダーをセッションに設定
import requests

with requests.Session() as s:
    s.headers.update({
        "User-Agent": "my-app/1.0",
        "X-API-Client": "session-demo"
    })
    r = s.get("https://httpbin.org/headers", timeout=10)
    h = r.json()["headers"]
    print("User-Agent:", h.get("User-Agent"))
    print("X-API-Client:", h.get("X-Api-Client"))
実行結果
User-Agent: my-app/1.0
X-API-Client: session-demo

すぐ使えるパターン

ここでは実務や学習でそのまま役立つ具体例を取り上げます。

連続リクエストをまとめて送る

同じホストへ複数回アクセスする処理では、1つのSessionを使い回すだけで時間短縮が期待できます。

目安として、手元の環境の一例を示します(値は環境で変わります)。

Python
# 単発requestsとSession使い回しの簡易計測
import time
import requests

URL = "https://httpbin.org/get"
N = 10

def no_session():
    start = time.perf_counter()
    for _ in range(N):
        requests.get(URL, timeout=10)
    return time.perf_counter() - start

def with_session():
    start = time.perf_counter()
    with requests.Session() as s:
        for _ in range(N):
            s.get(URL, timeout=10)
    return time.perf_counter() - start

t1 = no_session()
t2 = with_session()

print(f"no_session: {t1:.3f}s")
print(f"with_session: {t2:.3f}s")
print("speedup x", round(t1 / t2, 2))
実行結果
no_session: 9.075s
with_session: 2.520s
speedup x 3.6
注意

ネットワーク環境によって差は大きく異なりますが、多数回の呼び出しほど差が広がる傾向があります。

認証トークンをSessionに保持する

BearerトークンやAPIキーを1回設定しておけば、以降の全リクエストに適用できます。

安全な保管と最小権限の原則は別途守ってください。

Python
# 認証トークンをセッションに設定して使い回す
import requests

token = "abc123-example-token"  # 実際は安全な保管・取得方法を用いてください

with requests.Session() as s:
    s.headers.update({"Authorization": f"Bearer {token}"})
    r = s.get("https://httpbin.org/bearer", timeout=10)
    data = r.json()
    print("authenticated:", data.get("authenticated"))
    print("token echoed:", data.get("token"))
実行結果
authenticated: True
token echoed: abc123-example-token

JSON APIを何度も呼ぶときに効果大

JSON APIを短時間に何度も呼ぶ処理は、接続再利用の恩恵を受けやすいです。

下記はUUIDを複数回取得する例です。

Python
# JSON APIを繰り返し呼ぶ例(接続再利用で効率化)
import requests

with requests.Session() as s:
    uuids = []
    for _ in range(5):
        r = s.get("https://httpbin.org/uuid", timeout=10)
        uuids.append(r.json()["uuid"])
    print("uuids:", uuids[:3], "...")  # 先頭3つだけ表示
実行結果
uuids: ['2af3...', '9c41...', '8b27...'] ...

注意点とベストプラクティス

Sessionは作りっぱなしでも毎回作り直しでも良くありません

以下のポイントを押さえて、安全かつ効率的に使いましょう。

毎回Sessionを新規作成しない

ループ内でrequests.Session()を毎回生成すると、接続再利用ができず遅いだけでなく、リソースリークの原因にもなります。

処理単位(アプリ全体・バッチ1回分など)で1つを長めに使うのが基本です。

使い終わったら必ずcloseする

withの利用が最も簡単です。

明示的に管理する場合はtry/finallyで閉じるのが安全です。

Python
# 明示的にcloseを保証するパターン
import requests

s = requests.Session()
try:
    r = s.get("https://httpbin.org/get", timeout=10)
    print("status:", r.status_code)
finally:
    s.close()  # ここで確実に解放
実行結果
status: 200

セッションを広い範囲で使い回す(関数外で保持)

モジュールレベルに1つのセッションを用意し、関数から参照するパターンは実務でよく使われます。

アプリ終了時にcloseする工夫も入れておきます。

Python
# モジュール全体で1つのSessionを共有する例
import atexit
import requests

session = requests.Session()
session.headers.update({"User-Agent": "my-batch/2.0"})
atexit.register(session.close)  # プロセス終了時に自動close

def fetch_json(url: str):
    # ここでは同じsessionを使い回す
    resp = session.get(url, timeout=10)
    resp.raise_for_status()
    return resp.json()

if __name__ == "__main__":
    data = fetch_json("https://httpbin.org/get")
    print("url:", data["url"])
実行結果
url: https://httpbin.org/get
補足

高負荷環境ではプールサイズを増やす必要があるかもしれません。

HTTPAdapterで調整できます(出力なしの設定例)。

Python
# コネクションプールのサイズ調整(高度な例)
import requests
from requests.adapters import HTTPAdapter

s = requests.Session()
adapter = HTTPAdapter(pool_connections=100, pool_maxsize=100)  # 目安。要件に合わせて調整
s.mount("https://", adapter)
s.mount("http://", adapter)

# 以降、sで高並列・高頻度の呼び出しに対応しやすくなる

並列処理での共有は避ける

Sessionはスレッドセーフであると明示されていません

1つのSessionを複数スレッドで同時共有するのは避けスレッド(またはプロセス)ごとにSessionを作るのが基本です。

Python
# 良い例: ワーカーごとにSessionを作成する
from concurrent.futures import ThreadPoolExecutor, as_completed
import requests

def get_uuid():
    # 各ワーカー内でSessionを作成して破棄
    with requests.Session() as s:
        r = s.get("https://httpbin.org/uuid", timeout=10)
        return r.json()["uuid"]

uuids = []
with ThreadPoolExecutor(max_workers=3) as ex:
    futures = [ex.submit(get_uuid) for _ in range(5)]
    for f in as_completed(futures):
        uuids.append(f.result())

print("count:", len(uuids))
print("sample:", uuids[0][:8], "...")
実行結果
count: 5
sample: a1b2c3d4 ...

注意: マルチプロセスでも同様に、プロセスごとにSessionを生成してください。

フォーク後に親プロセスのSessionを使い回すのは避けましょう。

まとめ

requests.Sessionは「接続・ヘッダー・クッキーの再利用」によりAPI通信を高速化し、コードを簡潔にします

ポイントは次の通りです。

まず、同じホストに繰り返しアクセスする処理では必ずSessionを使うこと。

次に、使い終わったら必ずcloseすること。

そして、並列処理では共有せず、ワーカーごとにSessionを持つことです。

これらを守るだけで、多くのWeb・API開発において、パフォーマンスと可読性の両方を手に入れることができます。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!