💡 この記事でわかること
- requestsとは何か、WebAPIとどう関係するのか
- requestsのインストール方法(
pip) requests.get()でデータを取得し、response.json()で辞書として扱う方法- パラメータ付きのGETと、
requests.post()でのデータ送信 - ステータスコード・
timeout・例外を使った、安全な通信のしかた
1. requestsとは?
requestsとは、
👉 PythonからWeb上のサーバーにHTTPリクエストを送り、返ってきた結果を受け取るための外部ライブラリです。
💡 天気・地図・SNSなど、多くのサービスは「WebAPI」として機能を公開していて、決められたURLにアクセスすると、(多くの場合JSON形式で)データが返ってきます。requestsを使うと、そのアクセスを数行のコードで書けます。
標準ライブラリの urllib.request でも通信はできますが、requestsのほうが書き方が簡潔で読みやすいため、広く使われています。
この記事では、練習用の無料のダミーAPI「JSONPlaceholder」(https://jsonplaceholder.typicode.com)を例に使います。テスト用のデータを返してくれる公開サービスなので、実際のデータを壊す心配なく試せます。
👉 JavaScriptのfetch(API通信)についての記事はこちら
2. requestsをインストールする
requestsは標準ライブラリではないので、pip でインストールします。ターミナルで次のコマンドを実行してください。
python3 -m pip install requests👉 環境によっては python3 の代わりに python や py を使います。pip の使い方や外部ライブラリの考え方は、モジュールの記事で解説しています。
👉 Pythonのモジュール(importとpip)についての記事はこちら
👉 MacのHomebrewなどでインストールしたPythonでは、error: externally-managed-environment というエラーで、インストールできないことがあります。これは、OSやHomebrewが管理しているPythonに、勝手にパッケージを追加させないための仕組みです。その場合は、仮想環境を作って、その中でインストールします。
インストールできたら、ファイルの先頭で import requests と書けば使えるようになります。
3. GETでデータを取得する(requests.get)
Webからデータを取得するには requests.get(URL) を使います。戻り値の response には、サーバーからの返答が入っています。
import requests
response = requests.get("https://jsonplaceholder.typicode.com/posts/1", timeout=10)
print(response.status_code) # 200(成功)
data = response.json() # JSONを辞書に変換
print(data["id"]) # 1
print(data["title"]) # 投稿のタイトル(文字列)response.status_code:結果を表す数字(200は成功)response.text:返ってきた内容を、そのままの文字列で取得response.json():返ってきたJSONを、Pythonの辞書やリストに変換して取得
👉 WebAPIの多くはJSONで返ってくるので、response.json() で辞書にして、キーを指定して取り出す形が基本です。ここでは userId・id・title・body というキーが含まれています。
👉 timeout=10 は、10秒以内に応答がなければ諦める、という指定です。この理由は、あとの「エラー処理」で説明します。
4. パラメータ付きのGET(params)
「ユーザーIDが1の投稿だけ」のように条件を付けて取得するときは、URLの末尾に ?userId=1 のような「クエリパラメータ」を付けます。requestsでは、辞書を params に渡すだけで済みます。
import requests
params = {"userId": 1}
response = requests.get("https://jsonplaceholder.typicode.com/posts", params=params, timeout=10)
print(response.url) # https://jsonplaceholder.typicode.com/posts?userId=1
print(len(response.json())) # 10(userIdが1の投稿の数)👉 URLを自分で文字列連結するより、params に辞書で渡すほうが安全で読みやすいです。日本語や記号を含む値も、requestsが自動で正しい形に変換してくれます。複数の投稿が返ってくるときは、response.json() がリスト(辞書のリスト)になります。
5. POSTでデータを送信する(requests.post)
サーバーにデータを送るときは requests.post() を使います。送りたいデータは json に辞書で渡します。
import requests
payload = {"title": "テスト投稿", "body": "本文です", "userId": 1}
response = requests.post("https://jsonplaceholder.typicode.com/posts", json=payload, timeout=10)
print(response.status_code) # 201(作成できた)
print(response.json()) # {'title': 'テスト投稿', 'body': '本文です', 'userId': 1, 'id': 101}👉 json= で渡すと、辞書がJSON形式に変換されて送信されます。HTMLのフォームのような形式で送りたいときは、data= を使います。どちらで送るかは、そのAPIの仕様に合わせて選びます。
👉 JSONPlaceholderは練習用のサービスなので、送信したデータが実際に保存されることはありません(保存されたように見える返答が返ってくるだけです)。
6. ステータスコードとエラー処理
サーバーは、結果を「ステータスコード」という数字で返します。よく見るものは次のとおりです。
| ステータスコード | 意味 |
|---|---|
200 | 成功(OK) |
201 | 作成に成功(POSTなどでデータを作成したとき) |
400 | リクエストの内容が正しくない |
401 / 403 | 認証が必要 / アクセスが許可されていない |
404 | 指定したURLが見つからない |
500 | サーバー側でエラーが起きた |
💡 requestsは、404や500のようなエラーの返答が来ても、自動では例外を発生させません。そのまま処理を続けてしまうと、エラーの内容を正常なデータとして扱ってしまうことがあります。
そこで、raise_for_status() を呼びます。ステータスコードが400以上のときに、例外(HTTPError)を発生させてくれます。
import requests
response = requests.get("https://jsonplaceholder.typicode.com/posts/9999", timeout=10)
print(response.status_code) # 404
print(response.ok) # False(400以上のときはFalse)
response.raise_for_status() # 404なので、ここで例外(HTTPError)が発生する通信そのものの失敗(ネットワークがつながらない・時間切れなど)も起こりうるので、実際のコードでは次のように try-except で受け止めるのが基本形です。
import requests
url = "https://jsonplaceholder.typicode.com/posts/1"
try:
response = requests.get(url, timeout=10)
response.raise_for_status() # エラーのステータスなら例外にする
data = response.json()
except requests.exceptions.Timeout:
print("時間内に応答がありませんでした")
except requests.exceptions.HTTPError as e:
print(f"HTTPエラー: {e}")
except requests.exceptions.RequestException as e:
print(f"通信に失敗しました: {e}")
else:
print(data["id"]) # 1Timeout:timeoutで指定した時間内に応答がなかったHTTPError:raise_for_status()で、エラーのステータスコードを検出したRequestException:requestsの例外すべてをまとめた親(接続できないときのConnectionErrorなども含む)
👉 timeout は、必ず指定する習慣を付けましょう。指定しないと、相手のサーバーが応答しないときに、プログラムがいつまでも待ち続けて止まったように見えてしまうことがあります。また、例外を書く順番は「細かいもの(Timeout・HTTPError)を先に、親の RequestException を最後に」です。
7. ヘッダーとAPIキーを使う
APIによっては、「誰がアクセスしているか」を確認するために、ヘッダー(リクエストの付加情報)にAPIキーなどを付ける必要があります。ヘッダーは headers に辞書で渡します。
import os
import requests
api_key = os.environ["MY_API_KEY"] # 環境変数からAPIキーを読み込む
headers = {"Authorization": f"Bearer {api_key}"}
# https://api.example.com/... は説明用の架空のURLです
response = requests.get("https://api.example.com/data", headers=headers, timeout=10)👉 APIキーは、パスワードと同じくらい大切な情報です。コードに直接書かず、環境変数などから読み込む形にしてください。コードに直接書いたままGitHubなどに公開してしまうと、第三者に悪用される危険があります。
👉 認証の方式(Bearer を使うのか、別のヘッダー名を使うのかなど)はAPIごとに異なります。必ず、使うサービスの公式ドキュメントで確認してください。
8. 使うときの注意点・つまずきポイント
① 相手のサーバーに負担をかけない
ループで何百回もリクエストを送るなど、短時間に大量のアクセスをすると、相手のサーバーに負担がかかります。APIには、利用規約や「1分間に何回まで」といった利用制限が決められていることも多いので、事前に確認しましょう。連続で送る場合は、間に待ち時間を入れます。
import time
for i in range(1, 4):
# ここでリクエストを送る処理
time.sleep(1) # 1秒待ってから、次のリクエストへ② ModuleNotFoundError: No module named 'requests' と出る
requestsがインストールされていないか、pip でインストールしたPythonと、実行しているPythonが別のものになっている可能性があります。python3 -m pip install requests のように、実行に使うPythonと同じ python3 経由で pip を呼ぶと、揃えやすくなります。
③ ステータスを確認せず、いきなり response.json() を呼ぶ
エラーページ(HTMLなど)が返ってきているときに response.json() を呼ぶと、JSONとして読めずにエラーになります。raise_for_status() を先に呼んで、成功を確認してから json() を使う流れにすると、原因を見つけやすくなります。
9. requests 早見表
| 書き方 | できること |
|---|---|
requests.get(url, params=..., timeout=10) | データを取得する(条件はparamsで指定) |
requests.post(url, json=..., timeout=10) | JSON形式でデータを送信する |
requests.put() / requests.delete() | データの更新 / 削除(APIの仕様による) |
headers={...} | ヘッダー(APIキーなど)を付ける |
response.status_code | ステータスコードを取得する |
response.ok | ステータスが400未満かどうか(True/False) |
response.text | 返答を文字列で取得する |
response.json() | 返答のJSONを辞書・リストで取得する |
response.raise_for_status() | エラーのステータスなら例外を発生させる |
10. まとめ
- requestsは、PythonからWebAPIなどにアクセスするための外部ライブラリ(
python3 -m pip install requests) - データ取得は
requests.get()、送信はrequests.post()、JSONの返答はresponse.json()で辞書にできる - 条件は
params、送信するJSONはjson=、APIキーなどはheadersで渡す - 404などでも自動では例外にならないので、
raise_for_status()とtry-exceptで確認する timeoutは必ず指定し、APIキーはコードに直接書かず、相手のサーバーに負担をかけないようにする
👉 まずは、今回のJSONPlaceholderで「GETで取得して、辞書から値を取り出す」ところまで試してみてください。それができれば、天気予報や地図など、公開されているさまざまなWebAPIにも同じ流れで応用できます。