Python

【Python】Web API

環境

・M2 Mac
・macOS Monterey
・shell:zsh

API

API(「Application Programming Interface」の略。)は、アプリケーションが情報をやりとりする際のインターフェースとなる仕組みをいいます。

APIを利用することで、第三者が提供するサービス内の情報や機能を利用することができるようになります。

Web API

APIのうち、Webを介してHTTPベースで使用するAPIのことを「Web API」といいます。

「HTTP(Hyper Text Transfer Protocolの略。)とは、Webサーバー(情報や機能を提供するコンピュータおよびソフトウェア)とWebクライアント(Webサーバが提供する情報や機能にアクセスするためのソフトウェア。Webブラウザ等。)がデータを通信する方式をいいます。

APIは基本的にWeb APIを指すことがほとんどですので、以下Web APIを単純にAPIと表記します。

REST API

「RESTの4原則」に従って実装されたWeb APIを「RESTful API」(単に「REST API」ともいいます)といいます。

「RESTの4原則」とは、Webを外部から利用するための決まり事の一つです。なお、RESTは「REpresentational State Transfer」の略です。

RESTの4原則

  1. アドレス可能性:すべての情報が1つのURI(URLのより広い概念)で表現できる。
  2. ステートレス性:すべてのリクエストが完全に分離されている。サーバー側で履歴に基づいた処理を行わない。
  3. 接続性:情報に別の情報へのリンクを含めることができ、別の情報に接続することができる。
  4. 統一インターフェース:情報の操作はすべてHTTPメソッドを利用する。

APIの利用方法

APIを利用することを「APIを叩く」ともいいますが、APIを叩く上で以下の2種類の情報が必要となります。

  1. APIに接続するための情報:APIエンドポイント(接続先のURL)、APIキー(API登録時に取得)
  2. APIに送信する情報:リクエスト(HTTPメソッドの種類等)、ヘッダー(データの種類、APIキー等)、ボディ(送信したい情報等)

HTTP

ここでHTTPについて少し詳しく見ていきます。HTTPとは、WebサーバーとWebクライアントがデータを通信する方式です。

WebクライアントがWebサーバーに処理を依頼することを「リクエスト」といい、WebサーバーがWebクライアントに返答することを「レスポンス」といいます。

リクエスト及びレスポンスの際にやり取りするデータ構造は共に次のような構造となっています。APIの利用方法の項目の「②APIに送信する情報」と同じ構造になっていることに注目してください。

1行目 (リクエストの場合)リクエスト行(HTTPメソッド等)、
(レスポンスの場合)ステータス行(ステータスコード等)
複数行 ヘッダー(データの補足情報)
空白行 -
複数行 ボディ(データ本体)

また、HTTP通信において、Webサーバーにリクエストを送るための手段を「HTTPメソッド」といいます。HTTPメソッドには次のような機能が用意されています。

主なHTTPメソッド 機能
GET データを取得する。
POST データを作成する。
PUT データを更新する。
DELETE
データを削除する。

APIを叩く際は、このHTTPメソッドとリクエストの情報を一緒にAPI側に伝えることになります。

本項では、GETメソッドを用いて具体的なAPIを叩いていきます。

APIを叩く

ここでは、内閣府が公開している「地域経済分析システム(RESAS)」のAPIを叩いていきます。まずはAPIに接続するための情報「APIエンドポイント」と「APIキー」を取得します。「RESAS-APIのホームページに「RESAS-API仕様書はこちら」と書かれたリンクがありますので、そちらに移動します。このページから「API Endpoint」が「https://opendata.resas-portal.go.jp」であることがわかります。

また、APIキーは利用登録することで発行されるとも書かれています。通常、APIキーは何らかの利用登録の後、個別に発行されます。

さらに、GETメソッドが利用可能であること、レスポンスデータは「JSON形式」であることもわかりました。

次に、API詳細仕様も確認します。APIキーの設定方法が記載されています。APIキーをリクエストヘッダー「X-API-KEY」に設定する必要があります。

その他、パラメータの設定方法やエラーの内容も記載されていますので、確認しておきましょう。

今回は市区町村一覧を取得してみます。HPのサイドメニュー「市区町村一覧」を開いてください。「GET api/v1/cities」と書かれていますが、先ほど出てきたAPIエンドポイントと合わせた「https://opendata.resas-portal.go.jp/api/v1/cities」が、市区町村データのURL(エンドポイント)となります。

また、parametersとして「prefCode」に「都道府県コード」が設定できます。このパラメータは「Required(必須)」が「true」となっているため、必須のパラメータとなります。

なお、東京都の都道府県コードは「13」です。見つけにくいのですが、サイドメニューの「都道府県一覧」のページから見つけることができました。

これで「APIに接続するための情報」は一通り得られたことになります。

Requestsライブラリ

データ取得にはHTTPメソッドのGETメソッドを使いますが、PythonでHTTPメソッドを使用するには「requestsライブラリ」を使用します。

# requestsモジュールをインストールします。
% pip3 install requests

 

requestsライブラリでGETメソッドを使用するには、「requests.get(url)」とします。

少し脱線しますが、ここでrequestsライブラリ公式ドキュメントの「api.py」を確認してみます。get()関数は次のように定義されています。

def get(url, params=None, **kwargs):
    r"""Sends a GET request.
    ...(省略)...
    """

    return request("get", url, params=params, **kwargs)

 

get()関数は戻り値として「request()関数」を呼び出しています。「requests.get(url)」は「requests.request("get", url)」と同じ内容であることがわかりました(通常はrequests.getを使用します。)。他のHTTPメソッドも同様です。

つまり、request()関数にHTTPメソッドとそれに合わせたパラメータを指定しているということがわかります。

request()関数のdocstringには、それぞれのパラメータの説明があります。GETメソッドとPOSTメソッドで使用することが多いパラメータについて見ていきます。

def request(method, url, **kwargs):
    """
    ...(省略)...
    :param params: (optional) Dictionary, list of tuples or bytes to send
        in the query string for the :class:`Request`.
    :param data: (optional) Dictionary, list of tuples, bytes, or file-like
        object to send in the body of the :class:`Request`.
    :param json: (optional) A JSON serializable Python object to send in the body of the 
        :class:`Request`.
    :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
    ...(省略)...
     """

 

使用する主なメソッド 主なパラメータ 内容
GET params クエリパラメータ(URL内で「?」以降に付与されるパラメータ)を指定します。辞書型で指定します。
GET headers ヘッダに情報を追加します。辞書型で指定します。
POST data ボティとしてデータを送信します。JSON形式で指定します。
POST json ボディとしてデータを送信します。辞書型で指定します(自動的にJSON型に変換されます。)。

JSON

APIでは主に「JSON形式」で情報をやりとりします。JSONは「JavaScript Object Notation」の略で、JavaScriptのオブジェクトの表記方法を元にしたデータフォーマットをいいます。

Pythonの辞書と同じ記法ですが、キーは必ず「ダブルクォーテーション」で囲います。値には「文字列(ダブルクォーテーションで囲うこと)、数値、null(※)、bool値、オブジェクト(JSON形式のデータ)、配列(リスト)」が使用できます。

※「null」は「値が存在しない」ことを表します。Pythonでは、「None型」(値「None」のみを持つ型)を使用して実装します。

プログラミング

それでは、実際にプログラミングをしていきます。

import requests

endpoint = 'https://opendata.resas-portal.go.jp/api/v1/cities'  # エンドポイントを指定します。
headers = {'X-API-KEY': '『APIキー』'}  # APIキーを指定します。
params = {'prefCode': 13}  # 東京都の都道府県コードを指定します。

response = requests.get(endpoint, headers=headers, params=params)
print(response.status_code)  # レスポンスのステータスコードを確認します。


# 実行結果
200

 

get()関数で取得したレスポンスオブジェクトの「json()メソッド」を使用することで、JSON形式のレスポンスを辞書に変換することができます。

print(response.json())


# 実行結果
{'message': None, 'result': [{'prefCode': 13, 'cityCode': '13101', 'cityName': '千代田区', 'bigCityFlag': '3'}, {'prefCode': 13, 'cityCode': '13102', 'cityName': '中央区', 'bigCityFlag': '3'}, {'prefCode': 13, 'cityCode': '13103', 'cityName': '港区', 'bigCityFlag': '3'}, 
...(省略)...
 {'prefCode': 13, 'cityCode': '13421', 'cityName': '小笠原村', 'bigCityFlag': '0'}]}

 

VScodeでJSONデータの整形

レスポンスの内容そのままでは見ずらいので、得られた辞書データをVSCodeを使って見やすく整形します。

VSCodeで「.json」拡張子のファイルを作成し、実行結果を貼り付けます。

次に、以下のショートカットを順番に実行します。

  1. command + A (文字列全体を選択状態にします。)
  2. command + K 、 command + F (拡張子のデータ形式に応じて整形します。)

すると以下のように見やすくJSON形式で整形されます。

{'message': None, 'result': [
        {'prefCode': 13, 'cityCode': '13101', 'cityName': '千代田区', 'bigCityFlag': '3'
        },
        {'prefCode': 13, 'cityCode': '13102', 'cityName': '中央区', 'bigCityFlag': '3'
        },
        {'prefCode': 13, 'cityCode': '13103', 'cityName': '港区', 'bigCityFlag': '3'
...(省略)...

 

このデータから、「result」キーの値に市区町村データが辞書形式でリストとして格納されており、さらに各要素の「cityName」キーに市区町村名がマッピングされていることがわかります。

最後に、市区町村名のみ抽出するようプログラムを修正して完了です。

import requests

endpoint = 'https://opendata.resas-portal.go.jp/api/v1/cities'  # エンドポイントを指定します。
headers = {'X-API-KEY': 'D5nwPXRjUxwwmjzCRD4oY3Sly16LV8hX6xJmN7b4'}  # APIキーを指定します。
params = {'prefCode': 13}  # 都道府県コードを指定します。

response = requests.get(endpoint, headers=headers, params=params)

# 以下が追記プログラムです。
result = response.json()['result']  # resultキーの値を変数resultに渡します。

for i in range(len(result)):
    print(result[i]['cityName'])  # 市区町村毎のデータのうち、cityNameキーの値を表示します。


# 実行結果
千代田区
中央区
港区
新宿区
文京区
台東区
...(省略)...