Python | 1 日 120 分 × 7 日アプリ学習:API取得アプリ(requests使用)(中級編)

Web APP Python
スポンサーリンク

2日目のゴール

2日目のテーマは
「requests を“ちゃんとした道具”として扱えるようになる」 ことです。

1日目は、

URLにアクセスする
ステータスコードを見る
JSONを response.json() で辞書として扱う

ここまで行きました。

2日目では一歩進んで、

タイムアウトや通信エラーに備える
try / except で「落ちないAPIコード」にする
クエリパラメータやヘッダーを整理して書く
「APIを叩く関数」を自分で設計する

という、「実戦で使える requests の書き方」に近づいていきます。


なぜエラー処理が必要なのか

APIは「こっちの都合」では動いてくれない

ローカルの計算なら、
自分のPCの中だけで完結します。

でも、APIは違います。

相手のサーバーが落ちているかもしれない
ネットワークが不安定かもしれない
レスポンスが返ってくるまで異常に時間がかかるかもしれない
URLやパラメータを自分が間違えているかもしれない

つまり、
「うまくいかない可能性」が常にある世界 です。

だからこそ、

ステータスコードを確認する
タイムアウトを設定する
例外(Exception)をキャッチする

という「守りのコード」が大事になります。


タイムアウトを付けるという発想

いつまでも待ち続けるコードは危険

何も指定しない requests.get(url) は、
基本的に「レスポンスが返ってくるまで待ち続ける」動きになります。

もしサーバーが固まっていたら?
もしネットワークが詰まっていたら?

あなたのプログラムも、
ずっと固まったままになります。

そこで使うのが timeout です。

import requests

url = "https://jsonplaceholder.typicode.com/todos/1"

response = requests.get(url, timeout=5.0)
Python

この timeout=5.0 は、

「最大5秒だけ待つ。5秒経っても返ってこなかったらエラーにする」

という意味です。

ここで重要なのは、

タイムアウトは「秒数」で指定する
短すぎるとすぐエラーになる
長すぎると固まっているのと変わらない

というバランス感覚です。

とりあえず、
「APIを叩くときは timeout を付ける」
という習慣だけ、2日目で身につけておきましょう。


requests の例外をちゃんと受け止める

失敗したときに「落ちないコード」にする

timeout を付けると、
タイムアウトしたときに requests.exceptions.Timeout という例外が発生します。

また、ネットワークの問題などで
requests.exceptions.RequestException という例外が出ることもあります。

これらを try / except で受け止めることで、
プログラム全体が落ちずに「エラーメッセージを出して終わる」
という、まともな振る舞いになります。

import requests

def fetch_todo(todo_id):
    url = f"https://jsonplaceholder.typicode.com/todos/{todo_id}"

    try:
        response = requests.get(url, timeout=5.0)
    except requests.exceptions.Timeout:
        print("タイムアウトしました。サーバーの応答が遅いようです。")
        return
    except requests.exceptions.RequestException as e:
        print("通信エラーが発生しました。詳細:", e)
        return

    if response.status_code != 200:
        print("取得に失敗しました。ステータスコード:", response.status_code)
        return

    try:
        data = response.json()
    except ValueError:
        print("レスポンスをJSONとして解釈できませんでした。")
        return

    print("ID:", data["id"])
    print("タイトル:", data["title"])
    print("完了フラグ:", data["completed"])
Python

ここでの重要ポイントを深掘りします。

try: の中に「失敗するかもしれない処理」を入れる
except requests.exceptions.Timeout: でタイムアウト専用の処理を書く
except requests.exceptions.RequestException as e: でその他の通信エラーをまとめて受ける
response.json() も失敗する可能性があるので、別の try / except で囲む

これで、

ネットワークエラー
タイムアウト
ステータスコード異常
JSON解釈エラー

といった「よくある失敗パターン」を、
全部「落ちない形」で処理できるようになります。


「APIを叩く関数」を1つの型として覚える

何度も使える“テンプレート”を作る

2日目で、ぜひ頭に入れてほしいのが
「API取得関数の型」 です。

例えば、こんな形です。

import requests

def get_json(url, params=None, timeout=5.0):
    try:
        response = requests.get(url, params=params, timeout=timeout)
    except requests.exceptions.Timeout:
        print("タイムアウトしました。")
        return None
    except requests.exceptions.RequestException as e:
        print("通信エラー:", e)
        return None

    if response.status_code != 200:
        print("エラー応答です。ステータスコード:", response.status_code)
        return None

    try:
        return response.json()
    except ValueError:
        print("JSONとして解釈できませんでした。")
        return None
Python

この関数は、

URLとパラメータを受け取る
GETリクエストを送る
エラーを全部受け止める
成功したら JSON を辞書やリストとして返す
失敗したら None を返す

という「型」になっています。

これを使うと、
APIを叩く側のコードがとてもシンプルになります。

def fetch_todo(todo_id):
    url = f"https://jsonplaceholder.typicode.com/todos/{todo_id}"
    data = get_json(url)

    if data is None:
        print("TODOの取得に失敗しました。")
        return

    print("ID:", data["id"])
    print("タイトル:", data["title"])
    print("完了フラグ:", data["completed"])
Python

ここでの本質は、

「エラー処理を毎回ベタ書きしない」
「API取得の共通処理を1か所にまとめる」

という設計の考え方です。


クエリパラメータを「辞書」で渡すメリット

URLを文字列でゴリゴリ書かない

1日目でも少し触れましたが、
クエリパラメータは params で渡すのが基本です。

import requests

url = "https://jsonplaceholder.typicode.com/todos"
params = {"userId": 1}

response = requests.get(url, params=params, timeout=5.0)
Python

これを、さっきの get_json に組み合わせるとこうなります。

def fetch_todos_by_user(user_id):
    url = "https://jsonplaceholder.typicode.com/todos"
    params = {"userId": user_id}
    data = get_json(url, params=params)

    if data is None:
        print("TODO一覧の取得に失敗しました。")
        return

    print(f"userId={user_id} のTODOは {len(data)} 件あります。")
    for item in data[:5]:
        print("-", item["id"], item["title"])
Python

ここでの大事なポイントは、

URL文字列を "https://.../todos?userId=1" のように自分で組み立てない
params に辞書で渡すことで、
requests が正しくエンコードしてくれる

ということです。

パラメータが増えれば増えるほど、
params を使うメリットは大きくなります。


ヘッダーを付けるという発想

「名札」や「鍵」を一緒に送るイメージ

多くのAPIは、
「APIキー」や「トークン」をヘッダーに付けて送る必要があります。

例えば、こんなイメージです。

headers = {
    "Authorization": "Bearer xxxxxxxx",
    "Accept": "application/json",
}
Python

requests.getheaders を渡すと、
HTTPリクエストのヘッダーとして送ってくれます。

response = requests.get(url, headers=headers, timeout=5.0)
Python

2日目では、
実際のAPIキーを使わなくてもいいので、

「ヘッダー = リクエストに付ける追加情報」
というイメージだけ持っておいてください。

get_json を少し拡張すると、こうなります。

def get_json(url, params=None, headers=None, timeout=5.0):
    try:
        response = requests.get(url, params=params, headers=headers, timeout=timeout)
    except requests.exceptions.Timeout:
        print("タイムアウトしました。")
        return None
    except requests.exceptions.RequestException as e:
        print("通信エラー:", e)
        return None

    if response.status_code != 200:
        print("エラー応答です。ステータスコード:", response.status_code)
        return None

    try:
        return response.json()
    except ValueError:
        print("JSONとして解釈できませんでした。")
        return None
Python

これで、
APIキーが必要なAPIにも、そのまま対応できる形になります。


2日目のミニアプリ:ユーザーID別TODO一覧ビューア

仕様

ユーザーIDを入力する
そのユーザーのTODO一覧をAPIから取得する
最初の数件だけタイトルを表示する
エラーが起きても落ちずにメッセージを出す

コンソール版で、「エラーに強いAPIクライアント」の感覚を掴みます。

import requests

def get_json(url, params=None, timeout=5.0):
    try:
        response = requests.get(url, params=params, timeout=timeout)
    except requests.exceptions.Timeout:
        print("タイムアウトしました。")
        return None
    except requests.exceptions.RequestException as e:
        print("通信エラー:", e)
        return None

    if response.status_code != 200:
        print("エラー応答です。ステータスコード:", response.status_code)
        return None

    try:
        return response.json()
    except ValueError:
        print("JSONとして解釈できませんでした。")
        return None

def fetch_todos_by_user(user_id):
    url = "https://jsonplaceholder.typicode.com/todos"
    params = {"userId": user_id}
    data = get_json(url, params=params)

    if data is None:
        print("TODO一覧の取得に失敗しました。")
        return

    print(f"userId={user_id} のTODOは {len(data)} 件あります。")
    print("最初の5件を表示します。")
    for item in data[:5]:
        print(f"- [{item['id']}] {item['title']} (完了: {item['completed']})")

def main():
    print("ユーザーID別 TODO 一覧ビューア")
    user_id_str = input("ユーザーIDを入力してください(例: 1): ").strip()

    if not user_id_str.isdigit():
        print("数字を入力してください。")
        return

    user_id = int(user_id_str)
    fetch_todos_by_user(user_id)

if __name__ == "__main__":
    main()
Python

ここには、2日目で押さえたい要素が詰まっています。

get_json という共通関数で、
タイムアウト・通信エラー・ステータスコード・JSONエラーをまとめて処理している。
APIを使う側(fetch_todos_by_user)は、
「URLとパラメータを渡して、返ってきたデータを使う」ことだけに集中できている。


2日目で絶対に押さえてほしい本質

「APIは失敗する前提で書く」ことが“中級”の入り口

今日いちばん大事なのは、

APIはいつでも成功するわけではない
だから、失敗したときの振る舞いを最初から決めておく
そのために、timeouttry / except をセットで使う

という感覚です。

技術的なキーワードをまとめると、

requests.get(url, timeout=秒数) で「待ち時間の上限」を決める
requests.exceptions.TimeoutRequestExceptionexcept で受ける
response.status_code を見て 200 以外を弾く
response.json() も失敗することがあるので try / except で囲む
API取得処理を get_json のような共通関数にまとめる

ここまでできると、
あなたのAPIコードはもう「初心者の一発芸」ではなく、
「壊れにくい道具」 になっています。

3日目以降は、
このAPI取得ロジックを tkinter のボタンイベントと組み合わせて、
「ボタンを押したらAPIからデータを取ってきて、画面に表示する」
という、いよいよ“アプリらしいAPIクライアント”に進んでいきます。

まずは今日の get_json を、自分の「APIテンプレ」として
コピペしながら育てていってください。
ここを自分の型にできたら、APIの世界は一気に楽になります。

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