Python | Web / API:JSON API

Python Python
スポンサーリンク

概要(JSON APIは「辞書のやり取り」で短く、安全に)

JSON APIは、サーバとクライアントがJSON(辞書・配列ベースのテキスト)を送受信する仕組みです。Pythonでは標準のjsonモジュールとrequestsを使えば、辞書のまま送って辞書で受け取れます。重要なのはステータスと例外の確認、タイムアウト、Content-Typeの整合、文字エンコード、ページネーションや認証の型を押さえることです。


基本の読み書き(ここが重要)

GETでJSONを取得して辞書として扱う

import requests

url = "https://httpbin.org/json"
resp = requests.get(url, timeout=5)
resp.raise_for_status()            # 4xx/5xx を例外化
data = resp.json()                 # JSON → dict/list
print(type(data), list(data.keys()))
Python

json()を使えば文字列処理なしでPythonの辞書・リストになります。失敗を見落とさないためにraise_for_statusを必ず呼び、timeoutで待ちすぎを防ぎます。

POSTでJSONを送る(Content-Typeは自動)

import requests

payload = {"user": "taro", "score": 88}
resp = requests.post("https://httpbin.org/post", json=payload, timeout=5)
resp.raise_for_status()
echo = resp.json()                 # サーバ側の受信内容が返ってくる例
print(echo["json"])
Python

json=を使うとContent-Type: application/jsonが自動で付きます。APIはほぼこの形が基本です。

クエリパラメータで条件を付ける

import requests

params = {"q": "python", "page": 2, "lang": "ja"}
resp = requests.get("https://httpbin.org/get", params=params, timeout=5)
resp.raise_for_status()
print(resp.url)                    # 安全にエンコードされたURL
print(resp.json()["args"])         # サーバが受け取ったクエリ
Python

paramsへ辞書を渡すと日本語やスペースも自動エンコードされ、手書き連結の事故が避けられます。


レスポンスの検証と文字コード(信頼できるデータに整える)

Content-TypeでJSONかを確かめる

import requests

r = requests.get("https://httpbin.org/anything", timeout=5)
r.raise_for_status()
ctype = r.headers.get("Content-Type", "")
if "application/json" in ctype:
    data = r.json()
else:
    text = r.text
    print("JSON以外の応答:", ctype, text[:120])
Python

APIがエラー時にHTMLを返すケースがあります。Content-Typeを見てからjson()を呼ぶと安全です。

文字化け対策(テキスト応答時)

import requests

r = requests.get("https://httpbin.org/html", timeout=5)
r.encoding = r.apparent_encoding     # 推定で上書き
print(r.text[:100])
Python

JSONでは通常UTF-8ですが、HTMLやCSVなどのテキスト応答はencodingの明示が有効です。


エラーハンドリングと再試行(壊れない通信の型)

代表的な例外の捕捉

import requests

try:
    r = requests.get("https://httpbin.org/status/500", timeout=5)
    r.raise_for_status()
except requests.Timeout:
    print("タイムアウト")
except requests.ConnectionError:
    print("接続エラー")
except requests.HTTPError as e:
    print("HTTPエラー:", e)
except requests.RequestException as e:
    print("その他の例外:", e)
Python

Timeout、ConnectionError、HTTPErrorを押さえ、最後にRequestExceptionで網羅します。

セッションと簡易リトライ(一時不調への備え)

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

session = requests.Session()
retry = Retry(total=3, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504])
session.mount("https://", HTTPAdapter(max_retries=retry))

resp = session.get("https://httpbin.org/status/503", timeout=5)
print(resp.status_code)
Python

同一設定を使い回すSessionは認証やクッキーにも有効です。POSTのリトライは副作用に注意し、APIがidempotency-keyに対応している場合のみ積極利用します。


実務の勘所(認証・ページネーション・日付・バリデーション)

認証ヘッダーを付ける

import requests

headers = {"Authorization": "Bearer YOUR_TOKEN", "Accept": "application/json"}
r = requests.get("https://httpbin.org/bearer", headers=headers, timeout=5)
print(r.status_code)
Python

機密はクエリではなくヘッダーで渡すのが基本です。AcceptでJSON応答を明示します。

ページネーションで分割取得

import time, requests

base = "https://httpbin.org/get"
for page in range(1, 4):
    r = requests.get(base, params={"page": page, "limit": 100}, timeout=5)
    r.raise_for_status()
    print("page:", page, "url:", r.url)
    time.sleep(0.2)  # レート制限対策の小休止
Python

pageとlimitを基礎に、サーバのレート制限ヘッダー(Retry-Afterなど)があれば指示に従います。

日付・時刻はISO 8601で文字列化

from datetime import datetime
import requests

p = {"from": datetime(2025, 12, 1, 9, 0, 0).isoformat()}  # "2025-12-01T09:00:00"
r = requests.get("https://httpbin.org/get", params=p, timeout=5)
print(r.url)
Python

API仕様に合わせ、ISO 8601を使うと互換性が高くなります。

バリデーションでスキーマを守る(最小限のチェック)

import requests

r = requests.get("https://httpbin.org/json", timeout=5)
d = r.json()
assert "slideshow" in d and isinstance(d["slideshow"], dict), "期待するキーがない"
Python

型や必須キーを軽く検証してから処理すると、不完全な応答でのクラッシュを防げます。


JSONのファイル連携(保存・読み込み・整形)

保存と読み込みはjson.dump / json.load

import json, requests
from pathlib import Path

out = Path("data/resp.json")
out.parent.mkdir(parents=True, exist_ok=True)

r = requests.get("https://httpbin.org/json", timeout=5)
r.raise_for_status()
data = r.json()

with out.open("w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)  # 日本語をそのまま保存・見やすく整形

with out.open("r", encoding="utf-8") as f:
    loaded = json.load(f)
print(list(loaded.keys()))
Python

ensure_ascii=Falseで日本語が「\uXXXX」にならず、indentで人間が読める形に整えられます。


よくある落とし穴の回避(形式・タイムアウト・冪等性)

ボディ形式の取り違えを避ける

APIがJSONを要求しているのにdata=でフォーム送信すると失敗します。JSONはjson=、フォームはdata=を使い分けます。

タイムアウト未指定で処理が固まるのを避ける

timeoutを毎回指定します。通常は数秒、重い処理や大容量では長めに調整します。

失敗の見落としを避ける

status_codeの目視だけでは漏れます。raise_for_statusを必ず呼び、例外で検知します。

非冪等なPOSTの再試行で二重実行を避ける

登録や課金など副作用のあるAPIは、むやみにリトライしないか、idempotency-keyを使う設計に合わせます。


例題で身につける(定番から実務まで)

例題1:GETでJSON取得→キー取り出し

import requests

resp = requests.get("https://httpbin.org/json", timeout=5)
resp.raise_for_status()
d = resp.json()
print(d["slideshow"]["title"])
Python

例題2:POSTでJSON送信→応答検証

import requests

payload = {"user": "taro", "roles": ["admin", "editor"]}
resp = requests.post("https://httpbin.org/post", json=payload, timeout=5)
resp.raise_for_status()
print(resp.json()["json"])
Python

例題3:クエリで条件指定→安全なURL

import requests

params = {"q": "東京 天気", "lang": "ja", "page": 1}
r = requests.get("https://httpbin.org/get", params=params, timeout=5)
r.raise_for_status()
print(r.url)           # 自動エンコード済み
print(r.json()["args"])
Python

例題4:Session+認証+リトライ

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

session = requests.Session()
session.headers.update({"Authorization": "Bearer TOKEN", "Accept": "application/json"})
session.mount("https://", HTTPAdapter(max_retries=Retry(total=3, backoff_factor=0.5, status_forcelist=[500, 503])))

r = session.get("https://httpbin.org/json", timeout=5)
r.raise_for_status()
print(r.json())
Python

まとめ

JSON APIは「辞書を送って辞書で受け取る」感覚で扱えるため、Pythonとの相性が抜群です。成功可否はraise_for_statusで例外化し、timeoutで固まりを防ぐ。Content-Typeを確認してjson()を安全に使い、クエリはparamsで自動エンコード。認証ヘッダー、ページネーション、ISO日付、軽いバリデーション、json.dump/loadでの保存を型として身につければ、初心者でも短く、壊れないWeb/API連携が自然に書けます。

タイトルとURLをコピーしました