Web APIにアクセスする最短の方法がrequestsです。
この記事では、Python初心者の方でも迷わないように、GETとPOSTの基本からエラー対処、タイムアウト、APIキーの扱いまで順番に解説します。
まずは最小のサンプルで感覚を掴み、その後で実践的な書き方へ進みます。
requestsの準備と基本
requestsのインストール(pip)
requestsは標準ライブラリではありません。
最初にpipでインストールします。
可能なら仮想環境を用意するとプロジェクトごとの依存関係が分離できて便利です。
# 1. 仮想環境の作成と有効化(任意)
python -m venv .venv
# Windows
.\.venv\Scripts\activate
# macOS/Linux
source .venv/bin/activate
# 2. requestsのインストール
pip install -U requests
# 3. バージョンの確認
python -c "import requests; print(requests.__version__)"
よくある疑問
インストールしたのにimportできない場合は、仮想環境が有効化されているかや、複数のPython環境が混在していないかを確認します。
APIリクエストの流れ(送信→受信)
API通信は大きく以下の流れで進みます。
- クライアント側でURLとHTTPメソッド(GETやPOST)を決め、必要なヘッダーやデータを添えて送信します。
- ネットワークを経由してAPIサーバーが受け取り、処理を行います。
- サーバーは結果をHTTPレスポンスとして返します。
- クライアントはステータスコードやレスポンス本文(JSONやHTMLなど)を読み取り、アプリで活用します。
用語の整理
- エンドポイント: APIが用意するURLのことです。例は
https://api.example.com/v1/users
です。 - HTTPメソッド: GETやPOSTなどの動作の種類です。GETは取得、POSTは作成や送信に使います。
- ヘッダー: 追加情報を運ぶ欄です。例は
Content-Type
やAuthorization
です。 - ボディ: POSTなどで送る実際のデータ部分です。
- クエリパラメータ: URLの
?
以降で指定するkey=value
の形式の値です。
レスポンスの基本(JSON/テキスト/ステータス)
requestsのレスポンスオブジェクトResponse
でよく使う属性やメソッドの概要です。
項目 | 説明 |
---|---|
status_code | 数値のステータスコードです。200台は成功、400台はクライアントエラー、500台はサーバーエラーです。 |
headers | サーバーから返るHTTPヘッダーの辞書です。 |
text | 文字列として本文を返します。encodingが重要です。 |
content | 生のバイナリデータです。画像やPDFの保存に使います。 |
json() | 本文をJSONとしてパースします。JSONでないと例外が発生します。 |
ok | ステータスが200〜399ならTrueです。 |
raise_for_status() | 4xx/5xxのときHTTPError を投げます。 |
url | 最終的にアクセスしたURLです。リダイレクト後のURLが入ります。 |
elapsed | 通信にかかった時間です。 |
JSONを扱うAPIではresponse.json()
が基本ですが、JSONでないレスポンスに対して実行すると例外になる点には注意します。
GETリクエストの送り方
最小のGET
最小のGETはURLだけでOKです。
まずは動作確認してみましょう。
# 最小のGETリクエストの例
# httpbinはテスト用の公開APIです。送った内容をそのまま返してくれます。
import json
import requests
url = "https://httpbin.org/get"
resp = requests.get(url) # URLだけで送信
print("status:", resp.status_code)
# JSONとして受け取り、重要な一部だけ整形して表示
data = resp.json()
print(json.dumps({"url": data["url"], "args": data["args"]}, ensure_ascii=False, indent=2))
{
"status": 200,
"url": "https://httpbin.org/get",
"args": {}
}
- まずは成功(200)が出ることを確認しましょう。
- shapeだけ見たいときはすぐに
resp.json()
を使うと分かりやすいです。
クエリパラメータ(params)
URLに?q=python&page=1
のようなクエリを付けたい場合はparams
引数を使います。
# クエリパラメータ付きGET
import requests
url = "https://httpbin.org/get"
params = {"q": "python", "page": 1}
resp = requests.get(url, params=params)
print("final url:", resp.url) # エンコード後の最終URL
print("args:", resp.json()["args"]) # サーバーが受け取ったクエリ
final url: https://httpbin.org/get?q=python&page=1
args: {'page': '1', 'q': 'python'}
ヘッダーの指定(headers)
任意のHTTPヘッダーを付けるにはheaders
引数を使います。
# ヘッダーを付与して送信
import requests
url = "https://httpbin.org/headers"
headers = {
"User-Agent": "my-sample-app/0.1",
"Accept": "application/json",
"X-Request-Source": "tutorial"
}
resp = requests.get(url, headers=headers)
data = resp.json()
# サーバーが受け取ったヘッダーの一部を確認
received = {
"User-Agent": data["headers"].get("User-Agent"),
"Accept": data["headers"].get("Accept"),
"X-Request-Source": data["headers"].get("X-Request-Source"),
}
print(received)
{'User-Agent': 'my-sample-app/0.1', 'Accept': 'application/json', 'X-Request-Source': 'tutorial'}
JSONレスポンスの受け取り(json)
レスポンスがJSONのときはjson()
が最も簡単です。
JSONでないと例外が出るため、心配ならtry/exceptで囲みます。
# JSONの取得とパース
import requests
resp = requests.get("https://httpbin.org/json") # 固定のJSONを返すエンドポイント
try:
data = resp.json() # JSON以外なら例外が発生
print("title:", data.get("slideshow", {}).get("title"))
except ValueError as e:
print("JSONとして読み取れませんでした:", e)
title: Sample Slide Show
ステータスコードの確認(status_code)
処理の成否はstatus_code
やok
で判断できます。
失敗ならraise_for_status()
で例外にして上位でまとめて扱うのも定番です。
# ステータスコードの確認と基本的な分岐
import requests
resp = requests.get("https://httpbin.org/status/404")
print("status:", resp.status_code, "ok:", resp.ok)
if resp.ok:
print("成功:", resp.text[:60])
else:
print("失敗:", resp.status_code)
status: 404 ok: False
失敗: 404
ステータス | 意味の目安 |
---|---|
200 OK | 成功です。 |
201 Created | 新規作成が成功です。 |
204 No Content | 成功だが本文なしです。 |
400 Bad Request | リクエストの形式が不正です。 |
401 Unauthorized | 認証が必要または無効です。 |
403 Forbidden | 権限が足りません。 |
404 Not Found | URLやリソースが見つかりません。 |
429 Too Many Requests | レート制限に達しました。 |
500 Internal Server Error | サーバー側のエラーです。 |
エラーの簡単な扱い(try/except)
ネットワークは失敗する前提で考えるのが安全です。
タイムアウトや通信エラー、4xx/5xxを例外として扱うことで堅牢になります。
# エラーの基本パターン
import requests
try:
# わざと短いタイムアウトで遅延エンドポイントにアクセスしてTimeoutを誘発
resp = requests.get("https://httpbin.org/delay/3", timeout=0.5)
resp.raise_for_status() # 4xx/5xxはHTTPErrorに
print("成功:", resp.status_code)
except requests.exceptions.Timeout:
print("タイムアウトしました")
except requests.exceptions.HTTPError as e:
print("HTTPエラー:", e, "status:", getattr(e.response, "status_code", "?"))
except requests.exceptions.RequestException as e:
# 上記以外のrequests由来のエラーはこれで捕捉
print("通信エラー:", e)
タイムアウトしました
POSTリクエストの送り方
JSONを送信(json引数)
APIにJSONを送りたい場合、json=
引数を使うとContent-Type: application/json
が自動で付き、標準的なJSONエンコードが行われます。
# JSONをPOSTで送る
import requests
url = "https://httpbin.org/post"
payload = {"name": "Taro", "age": 20}
resp = requests.post(url, json=payload) # json= でOK
data = resp.json()
# サーバーが受け取ったJSONを確認
print("received json:", data["json"])
received json: {'name': 'Taro', 'age': 20}
補足
手動でJSON化する方法もありますが、json=
を使う方が簡単で安全です。
- 手動の例:
requests.post(url, data=json.dumps(obj), headers={"Content-Type":"application/json"})
フォームデータを送信(data)
Webフォームと同じapplication/x-www-form-urlencoded
形式で送るにはdata=
を使います。
# フォームデータの送信
import requests
url = "https://httpbin.org/post"
form = {"username": "alice", "password": "secret"}
resp = requests.post(url, data=form)
print("received form:", resp.json()["form"])
received form: {'password': 'secret', 'username': 'alice'}
ファイルを送信(files)
ファイルアップロードはfiles=
を使います。
multipart/form-data
で送信されます。
# ファイルアップロードの例(メモリ上で擬似ファイルを作成)
import io
import requests
url = "https://httpbin.org/post"
files = {
# ("ファイル名", ファイルオブジェクト, "MIMEタイプ")
"file": ("hello.txt", io.BytesIO(b"Hello from requests!"), "text/plain")
}
resp = requests.post(url, files=files)
data = resp.json()
# サーバーが受け取ったファイルの内容を確認
print(data["files"]["file"])
Hello from requests!
レスポンスの読み取り(JSON/テキスト)
レスポンスの種類に合わせて、JSONならjson()
、テキストならtext
を使い分けます。
# レスポンスのContent-Typeを見て読み方を分ける
import requests
resp = requests.post("https://httpbin.org/post", json={"ping": "pong"})
ctype = resp.headers.get("Content-Type", "")
if "application/json" in ctype:
print("as json:", resp.json().get("json"))
else:
print("as text:", resp.text[:120])
as json: {'ping': 'pong'}
失敗時の対処(基本)
POSTは特に入力エラーや認証エラーが起きやすいので、raise_for_status()
とエラー内容の記録をセットで行います。
# 失敗時の基本対処
import logging
import requests
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
url = "https://httpbin.org/status/400" # 400を返す
try:
resp = requests.post(url, json={"example": 1})
resp.raise_for_status()
except requests.exceptions.HTTPError as e:
# 問題の切り分けに役立つ情報だけ記録
logging.error("HTTPError status=%s url=%s", e.response.status_code, e.response.url)
ERROR: HTTPError status=400 url=https://httpbin.org/status/400
よく使うオプションと注意点
タイムアウト(timeout)の設定
requestsはデフォルトタイムアウトがありません。
必ずtimeout=
を付ける習慣を持つと、無限待ちを避けられます。
# タイムアウトの使い方
import requests
# 単一値は接続と読み取りの両方に適用
requests.get("https://httpbin.org/delay/2", timeout=3)
# タプルで(接続, 読み取り)を個別に設定
try:
requests.get("https://httpbin.org/delay/2", timeout=(0.3, 0.3))
except requests.exceptions.Timeout:
print("timeout!")
timeout!
APIキー認証(ヘッダー)
多くのAPIはヘッダーにキーを入れます。
キーはコードに直書きせず、環境変数で管理しましょう。
# APIキーを環境変数から読み、Authorizationヘッダーに設定
import os
import requests
API_KEY = os.getenv("MY_API_KEY") # 事前に環境変数を設定しておく
if not API_KEY:
raise RuntimeError("MY_API_KEY が設定されていません")
headers = {"Authorization": f"Bearer {API_KEY}"}
# 例: ダミーのエンドポイントを想定
# resp = requests.get("https://api.example.com/v1/me", headers=headers, timeout=5)
# print(resp.json())
print("ヘッダー例:", headers)
ヘッダー例: {'Authorization': 'Bearer ****************'}
APIキーをGitリポジトリにコミットしたり、ログに生で出力したりしないでください。
ベースURLとエンドポイントの考え方
大抵のAPIはベースURLと複数のエンドポイントで構成されます。
URLの組み立てと共通ヘッダーはSession
でまとめると便利です。
# ベースURLとSessionの活用
from urllib.parse import urljoin
import requests
BASE_URL = "https://httpbin.org/"
def build_url(path: str) -> str:
# urljoinはスラッシュの重複や欠落に強い
return urljoin(BASE_URL, path.lstrip("/"))
with requests.Session() as s:
# 共通のヘッダーやタイムアウトを1箇所で設定
s.headers.update({"User-Agent": "my-sample-app/0.1"})
# 例: エンドポイントを順に呼び出す
r1 = s.get(build_url("/get"), params={"q": "python"}, timeout=5)
r2 = s.get(build_url("json"), timeout=5)
print("r1:", r1.status_code, r1.url)
print("r2:", r2.status_code, r2.url)
r1: 200 https://httpbin.org/get?q=python
r2: 200 https://httpbin.org/json
Sessionは接続の再利用で高速化されるため、複数回のアクセスがあるときは特に有効です。
日本語の文字化け対策(encoding)
response.text
はresponse.encoding
でデコードされます。
サーバーが適切にエンコーディングを示さない場合は、apparent_encoding
で推定してから読むと改善します。
# 文字化け対策の基本パターン
import requests
resp = requests.get("https://httpbin.org/encoding/utf8")
# サーバーの宣言が怪しい場合は推定値を採用
if not resp.encoding or resp.encoding.lower() == "iso-8859-1":
resp.encoding = resp.apparent_encoding
text = resp.text # 適切なエンコーディングでデコードされた文字列
print(text.splitlines()[0]) # 先頭行だけ表示
<!DOCTYPE html>
JSONの場合はresp.json()
が自動処理してくれるため、通常はエンコーディングを意識しなくて構いません。
安全にログ出力するポイント(秘密情報を隠す)
デバッグ時に「何を送って何が返ったか」を記録すると便利ですが、秘密情報の露出は厳禁です。
マスク処理を挟みましょう。
# 安全なログ出力の例(トークンやクレデンシャルをマスク)
import logging
import re
import requests
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
SENSITIVE_HEADER_KEYS = {"authorization", "x-api-key", "proxy-authorization"}
def mask_headers(headers: dict) -> dict:
masked = {}
for k, v in headers.items():
if k.lower() in SENSITIVE_HEADER_KEYS:
masked[k] = "****" # 全面マスク
else:
masked[k] = v
return masked
def mask_body(body: str) -> str:
if body is None:
return ""
# "password":"xxx" のようなパターンをマスク(簡易版)
return re.sub(r'("password"\s*:\s*")([^"]+)(")', r'****', body)
url = "https://httpbin.org/post"
payload = {"username": "alice", "password": "secret"}
resp = requests.post(url, json=payload, headers={"Authorization": "Bearer TOKEN"})
safe_req_headers = mask_headers(resp.request.headers)
safe_req_body = mask_body(resp.request.body.decode() if isinstance(resp.request.body, bytes) else str(resp.request.body))
logging.info("request %s %s", resp.request.method, resp.request.url)
logging.info("headers %s", safe_req_headers)
logging.info("body %s", safe_req_body)
logging.info("response %s", resp.status_code)
INFO: request POST https://httpbin.org/post
INFO: headers {'User-Agent': 'python-requests/2.xx.x', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '****', 'Content-Length': '...','Content-Type': 'application/json'}
INFO: body {"username": "alice", "password": "****"}
INFO: response 200
アクセストークンやAPIキーをログやコンソールに生で表示しないことが重要です。
まとめ
この記事では、Pythonのrequestsを用いたGETとPOSTの基本操作を、インストールからレスポンス処理、エラー対処、タイムアウト、APIキー、文字化け対策、ログの安全化まで順に解説しました。
まずはrequests.get()
とrequests.post()
で成功体験を積むことが大切です。
次のステップでは、Session
で共通設定をまとめたり、raise_for_status()
と例外処理で堅牢性を高めたり、timeout=
でハングを防いだりと、実運用に耐える書き方へ発展させましょう。
最後に、秘密情報は直書きしない・見せないという基本を徹底すれば、requestsは強力で安全なAPIクライアントになります。