Python requests を使ったAPI呼び出しガイド:基礎から実践まで

Python tutorial - IT technology blog
Python tutorial - IT technology blog

APIの基本と、なぜPython requestsが重要なのか?

ITの仕事では、アプリケーション同士が「会話」することは日常的に行われています。外部サービスからデータを取得したり、別のプラットフォームに更新情報を送信したり、APIを通じてタスクを自動化する必要があるかもしれません。Pythonで日常業務を自動化することも、APIを介して行われることが多いでしょう。Python requestsは、これらを効率的に行うためのツールです。

当初、多くのプログラマーはPythonに組み込まれているurllibライブラリを思い浮かべるかもしれません。しかし、彼らはすぐにurllibが通常のHTTPタスクにはかなり複雑で扱いにくいことに気づきます。

問題は、urllibがパラメーターのエンコード、ヘッダーの管理、レスポンスのデコードなど、多くの低レベルな詳細を自分で処理する必要がある点にあります。これにより、特に初心者にとっては、コードが長く読みにくくなります。そこで、requestsライブラリが最適なソリューションとなります。

requestsは、PythonでのHTTPリクエスト送信を簡素化するために誕生しました。ウェブ通信の複雑さを巧みに隠し、HTTPプロトコルの詳細ではなくアプリケーションロジックに集中できるようにします。実際、その利便性と強力さから、APIと連携する必要があるほとんどのPythonプログラマーにとって、requestsは主要な選択肢となっています。

クイックスタート: 5分でAPIを呼び出す

イメージしやすいように、早速requestsから始めましょう。ターミナルを開いて、一緒に実践してみましょう。

ステップ1: requestsライブラリをインストールする

Pythonがインストールされていることを確認し、venvなどで環境構築を行った上で、次に、pipを使ってrequestsをインストールします。


pip install requests

ステップ2: 簡単なGETリクエストを送信する

GETリクエストはサーバーからデータを取得するために使われます。例えば、公開API(例:JSONPlaceholder)から記事リストを取得したいとしましょう。


import requests

# 公開APIへGETリクエストを送信
response = requests.get('https://jsonplaceholder.typicode.com/posts/1')

# HTTPステータスコードをチェック
if response.status_code == 200:
    # JSON形式のレスポンス内容を表示
    print("GET成功!")
    print(response.json())
else:
    print(f"エラーが発生しました: {response.status_code}")

とても簡単ですよね?わずか数行のコードで、APIからデータを正常に取得できました。

ステップ3: POSTリクエストでデータを生成する

POSTリクエストは通常、サーバーにデータを送信するために使われ、例えば新しい記事を作成する場合などです。


import requests

url = 'https://jsonplaceholder.typicode.com/posts'
new_post = {
    "title": "私の新しい投稿",
    "body": "requests APIに関する興味深いコンテンツ。",
    "userId": 1
}

# JSONデータを伴うPOSTリクエストを送信
response = requests.post(url, json=new_post)

# 結果をチェックして表示
if response.status_code == 201: # 201 Created
    print("POST成功!")
    print(response.json())
else:
    print(f"エラーが発生しました: {response.status_code}")

この例では、引数json=new_postが、requestsnew_post辞書を自動的にJSONにエンコードし、Content-Type: application/jsonヘッダーを設定するのに役立っています。非常に便利です!JSONの取り扱いを補助するオンラインツールも開発者の強い味方です。

詳細な説明: API通信の最適化

基本的なHTTPメソッド

HTTPには、リソースに対して実行したいアクションの種類を指定するための複数のメソッド(動詞)があります。

  • GET: データを取得します。
  • POST: 新しいデータを送信してリソースを作成します。
  • PUT: 既存のリソース全体を更新します。
  • PATCH: 既存のリソースの一部を更新します。
  • DELETE: リソースを削除します。

requestsは、requests.get()requests.post()requests.put()requests.patch()requests.delete()といった対応する関数を提供しています。

パラメーターとヘッダー

GETリクエストを送信する際、URLにパラメーターを渡す必要があることがよくあります。requestsparams引数を使用することでこれを簡潔に処理します。


import requests

# 例:userId = 1 の記事を取得
params = {'userId': 1}
response = requests.get('https://jsonplaceholder.typicode.com/posts', params=params)
print("User ID 1 の記事:")
for post in response.json():
    print(f"- {post['title']}")

ヘッダーは、コンテンツタイプや認証トークンなど、リクエストまたはレスポンスに関する重要なメタデータを含んでいます。カスタムヘッダーを簡単に追加できます。


import requests

headers = {
    'User-Agent': 'My Python App v1.0',
    'Accept': 'application/json'
}
response = requests.get('https://jsonplaceholder.typicode.com/posts', headers=headers)
print("レスポンスヘッダー:")
print(response.headers)

レスポンスの処理

requestsからのresponseオブジェクトは非常に強力です。いくつかの重要な属性を以下に示します。

  • response.status_code: HTTPステータスコード(200 OK、404 Not Found、500 Internal Server Errorなど)。
  • response.text: レスポンス内容を文字列として取得します。
  • response.json(): JSON内容をPythonオブジェクト(辞書/リスト)に変換します。内容が有効なJSONでない場合はエラーを報告します。
  • response.content: レスポンス内容をバイト列として取得します。バイナリファイル(画像、動画)を扱う場合に便利です。
  • response.headers: レスポンスのヘッダーを含む辞書です。

エラーと例外の処理

実際には、APIが常に期待通りの結果を返すとは限りません。status_codeを確認し、エラーケースを処理する必要があります。requestsは、非常に便利なメソッドresponse.raise_for_status()も提供しています。このメソッドは、status_codeがエラーコード(4xxまたは5xxから)の場合に自動的にエラー(HTTPErrorをraise)を報告し、問題を迅速に検出するのに役立ちます。


import requests

url_invalid = 'https://jsonplaceholder.typicode.com/non-existent-path'
url_valid = 'https://jsonplaceholder.typicode.com/posts/1'

try:
    response = requests.get(url_invalid)
    response.raise_for_status() # 404コードの場合、エラーをraise
    print("GET成功!")
except requests.exceptions.HTTPError as err:
    print(f"HTTPエラー: {err}")
except requests.exceptions.ConnectionError as err:
    print(f"接続エラー: {err}")
except requests.exceptions.Timeout as err:
    print(f"タイムアウトエラー: {err}")
except requests.exceptions.RequestException as err:
    print(f"一般的なエラー: {err}")

print("\n--- 有効なURLで再試行 ---")
try:
    response = requests.get(url_valid)
    response.raise_for_status()
    print("有効なGET成功!")
    print(response.json())
except requests.exceptions.RequestException as err:
    print(f"エラーが発生しました: {err}")

認証 (Authentication)

多くのAPIはアクセスに認証を必要とします。requestsは複数の方法をサポートしています。

  • Basic Authentication: ユーザー名とパスワードを使用します。
  • Bearer Token (OAuth2、JWTに基づくトークン): Authorizationヘッダーにトークンを送信します。

import requests

# Basic認証
# response = requests.get('https://api.example.com/user', auth=('username', 'password'))

# Bearer Token (現在最も一般的)
token = 'your_super_secret_bearer_token'
headers_with_auth = {
    'Authorization': f'Bearer {token}',
    'Content-Type': 'application/json'
}

# Bearer Tokenを要求するAPIを想定
# response = requests.get('https://api.example.com/data', headers=headers_with_auth)
# print(response.json())

print("これらはBasic認証とBearer Token認証の例です。")

上級編: requestsを深く理解する

セッション: 状態を維持する必要がある場合

APIと複数回やり取りする場合、TCP/IP接続の再確立、Cookieやヘッダーの処理を各リクエストごとに行うのは時間がかかります。requests.Sessionは、この問題を効率的に解決するために生まれました。

Sessionオブジェクトは、ヘッダー、Cookie、認証情報などの設定を複数のリクエストにわたって自動的に維持します。これは、一度ログインした後、認証を繰り返すことなく複数のタスクを実行する場合に特に役立ちます。


import requests

# セッションを作成
with requests.Session() as session:
    # セッションの共通設定 (例: デフォルトヘッダーの追加)
    session.headers.update({
        'User-Agent': 'My Custom Python Client',
        'Accept-Language': 'en-US,en;q=0.5'
    })

    # 最初のリクエストを送信 (例: ログイン、Cookieの取得)
    # login_data = {'username': 'test', 'password': 'password'}
    # login_response = session.post('https://api.example.com/login', json=login_data)
    # print(f"Login status: {login_response.status_code}")

    # 次のリクエストを送信。Cookieとヘッダーは保持されています。
    # data_response = session.get('https://api.example.com/protected_data')
    # print(f"Protected data status: {data_response.status_code}")
    # print("Current session cookies:", session.cookies.get_dict())

    # セッションメカニズムを示す公開APIの例
    print("\n--- 公開APIでのセッションの例 ---")
    resp1 = session.get('https://httpbin.org/cookies/set/sessioncookie/12345')
    print("リクエスト1後のCookie:", session.cookies.get_dict())

    resp2 = session.get('https://httpbin.org/cookies')
    print("リクエスト2後のCookie (リクエスト1から維持):")
    print(resp2.json())

requests.Session()を使用すると、パフォーマンスが向上するだけでなく、コードもより簡潔になります。これは、同じドメインに対して連続して多くのリクエストを送信する場合に顕著です。

タイムアウト: 無限の待機を避ける

APIを呼び出す際、サーバーの応答が遅かったり、応答がなかったりすることがあります。タイムアウトを設定しないと、アプリケーションが永久にハングアップする可能性があります。待機時間を制御するために、常にタイムアウトを設定してください。


import requests

try:
    # サーバーからの応答を最大5秒待機
    response = requests.get('https://api.github.com/events', timeout=5)
    print("タイムアウト付きGET成功。")
except requests.exceptions.Timeout:
    print("リクエストがタイムアウトしました。")
except requests.exceptions.RequestException as e:
    print(f"その他のエラーが発生しました: {e}")

プロキシ: 匿名性が必要な場合やファイアウォールを越える場合

ファイアウォールを越えたり、IPアドレスを隠したりするためにプロキシ経由でリクエストを送信する必要がある場合、requestsは非常によくサポートしています。


import requests

proxies = {
    'http': 'http://10.10.1.10:3128',
    'https': 'http://10.10.1.10:1080',
}

# response = requests.get('http://example.org', proxies=proxies)
print("上記のコードは、リクエストのプロキシを設定しています。")
print("注意:必要に応じてプロキシアドレスを実際のアドレスに変更してください。")

ファイルのアップロード (File Uploads)

APIにファイルを送信するには、files引数を使用します。requestsContent-Type: multipart/form-dataヘッダーを自動的に処理します。


import requests

# テスト用のダミーファイルを作成
with open('test_file.txt', 'w') as f:
    f.write('これはテストファイルの内容です。')

url = 'https://httpbin.org/post' # POSTリクエストをテストするためのAPI

with open('test_file.txt', 'rb') as f:
    files = {'file': f} # 'file' はAPIバックエンドが期待するフィールド名
    response = requests.post(url, files=files)

    if response.status_code == 200:
        print("ファイルのアップロード成功!")
        print(response.json()['files'])
    else:
        print(f"ファイルのアップロード中にエラーが発生しました: {response.status_code}")

実践的なヒント: 実際のプロジェクトからの経験

セッションの適切な使用法

10万件のレコードを処理するスクリプトを作成した際、接続の再利用(コネクションプーリング)がいかに重要であるかを学びました。一般的なアプローチは、レコードごとにrequests.get()またはrequests.post()を呼び出すことです。

しかし、これは多くのHTTP接続を作成して閉じ、リソースを無駄にし、処理速度を大幅に低下させます。代わりに、10万件のレコード処理全体でrequests.Session()を使用することで、TCP接続が再利用されます。これにより、レイテンシが大幅に削減され、クライアントとサーバーの両方の負荷も軽減されます。


import requests

def process_records_efficiently(record_ids):
    with requests.Session() as session:
        # ここでセッションを一度設定できます (ヘッダー、認証など)
        session.headers.update({
            'Authorization': 'Bearer my_token',
            'Content-Type': 'application/json'
        })

        for record_id in record_ids:
            try:
                # セッションを使用してリクエストを送信
                response = session.get(f'https://api.example.com/data/{record_id}', timeout=10)
                response.raise_for_status()
                print(f"レコード {record_id} の処理が成功しました。")
                # response.json() からデータを処理
            except requests.exceptions.RequestException as e:
                print(f"レコード {record_id} の処理中にエラーが発生しました: {e}")

# 使用例 (10万件のrecord_idsを想定)
# large_record_list = range(1, 100001)
# process_records_efficiently(large_record_list)
print("これは、Sessionを使用して大量のレコードを効率的に処理する方法を示しています。")

SSL証明書の検証 (SSL Certificate Verification)

デフォルトでは、requestsはHTTPSリクエストのSSL証明書を検証します。これはセキュリティ上非常に重要です。開発環境で自己署名証明書を持つAPIや自己ホスト型APIを扱う場合、verify=Falseを使用して検証を無効にできます。ただし、本番環境では絶対にこれを行わないでください!


import requests

# response = requests.get('https://bad-ssl.example.com/', verify=True) # デフォルトはTrue
# 開発環境でSSLエラーが発生し、ソースが確実な場合:
# response = requests.get('https://bad-ssl.example.com/', verify=False)
print("SSL検証に関する注意:本番環境では常にTrueにしてください。")

レート制限の処理 (Rate Limiting)

多くのAPIは、乱用を防ぐために、特定の期間に送信できるリクエストの数に制限(レート制限)を設けています。リクエストが多すぎると、APIは429 Too Many Requestsエラーを返します。この制限を尊重するために、リクエスト間に遅延(delay)を追加したり、tenacityなどのライブラリを使用してスマートに再試行(retry)したりしてください。


import requests
import time

def fetch_data_with_rate_limit(urls):
    for url in urls:
        try:
            response = requests.get(url)
            if response.status_code == 429:
                print("レート制限に達しました。60秒待機します...。")
                time.sleep(60) # しばらく待機
                # その後、このリクエストを再試行できます
                response = requests.get(url)
                response.raise_for_status()
            else:
                response.raise_for_status()
            print(f"URL {url} からのデータ: {response.json()['args']}") # httpbin.org/get APIを想定
        except requests.exceptions.RequestException as e:
            print(f"URL {url} からのデータ取得中にエラーが発生しました: {e}")
        time.sleep(1) # レート制限を避けるために各リクエスト間に1秒待機

# 例
# api_urls = ['https://httpbin.org/get?item=1', 'https://httpbin.org/get?item=2']
# fetch_data_with_rate_limit(api_urls)
print("time.sleep を使ったレート制限処理の例。")

リクエストとレスポンスのログ記録 (Logging Requests and Responses)

デバッグや開発の過程で、HTTPリクエストとレスポンスをログに記録することは非常に重要です。Pythonを用いたAIプロジェクトなどでも、詳細なログは問題解決に役立ちます。Pythonのloggingを設定して、requestsライブラリからの詳細情報を表示できます。


import requests
import logging

# requestsライブラリのログを有効にする
logging.basicConfig(level=logging.DEBUG)
#logging.getLogger("requests").setLevel(logging.DEBUG)
#logging.getLogger("urllib3").setLevel(logging.DEBUG)

# リクエストを送信
requests.get('https://httpbin.org/get')
print("\nrequestsのログを確認するにはコンソール出力を確認してください。")

結論

Pythonのrequestsライブラリは、APIを扱う上で不可欠なツールです。コードを簡潔で読みやすくするだけでなく、強力な機能を提供し、ほとんどすべてのウェブ通信シナリオを効率的に処理します。基本的なAPI呼び出しから、セッション管理、エラー処理、認証、パフォーマンス最適化といった高度なタスクまで、requestsは優れた能力を発揮します。

ここで共有した知識と経験を適用することで、ウェブサービスと効果的かつ信頼性高く連携するPythonアプリケーションを自信を持って構築できるようになるでしょう。AIプロジェクトなど、様々な領域でこの素晴らしいツールをマスターするために、今すぐ実践を始めてみましょう!

Share: