不動産情報ライブラリAPIから情報をGeoJSONやCSVで取得する【Python】

# 不動産情報ライブラリAPIとは

不動産情報ライブラリは、不動産の取引価格、地価公示等の不動産に関する情報を閲覧・取得できる国土交通省のWEBサイトです。
2024年4月よりサービス提供が開始されました。

**不動産情報ライブラリ : [公式リンク](https://www.reinfolib.mlit.go.jp/)**
**不動産情報ライブラリAPI ： [公式リンク](https://www.reinfolib.mlit.go.jp/help/apiManual/)** 

# 本記事の作成動機

不動産取引価格など有益な情報が取得できそうなこのAPIですが、リクエストの投げ方やレスポンスの形式がやや難解に感じたので、それらに対応しやすい形でデータ取得できるPythonスクリプトを書いてみました。

# 前準備
## APIキーの取得
このAPIを利用するには、事前にAPIキーの取得申請を行う必要があります。
下記リンクから必要事項を記入して申請を行うと、数日〜1週間くらいでAPIキーが取得できます。

**APIキー取得申請ページ：[公式リンク](https://www.reinfolib.mlit.go.jp/api/request/)**

APIキーが取得できたら、後述のPythonスクリプトでデータ取得をしてみます。

# Pythonによるデータ取得スクリプト

## 実行環境
- MacOS 14.4.1
- Python 3.9.6

## スクリプト

```python:Loader_MlitRealEstateInfoAPI.py
import requests
import gzip
import json
import csv

base_url = "https://www.reinfolib.mlit.go.jp/ex-api/external/" #ベースURL
api_type = "XIT001" #APIの種類を記入。後の出力ファイル名にも反映させるため変数として格納しておく。

mlit_url = base_url + api_type #リクエスト用URL
mlit_params = {"year": "2015", "area": "12"} #パラメータの設定 例："response_format": "geojson", "z": "13", "x": "7280", "y": "3219", "from": "20223", "to": "20234"

mlit_key = input("Input your API key: ") #コマンドライン上でAPIキーの入力を求める
mlit_headers = {"Ocp-Apim-Subscription-Key": mlit_key, "Accept-Encoding": "gzip"} #APIキーをリクエストヘッダー用に格納する。gzipのレスポンスを許可する。

response = requests.get(mlit_url, headers=mlit_headers, params=mlit_params) #APIへリクエストを投げて、結果を変数に格納する

#APIからの応答に成功した場合の処理
if response.status_code == 200:
    #Content-Encodingヘッダーを確認してgzip形式であるかどうかを判断する
    if response.headers.get('Content-Encoding') == 'gzip':
        print("Response data is gzip encoded.")
        #gzip形式の場合は中身の解凍を試行する
        try:
            decoded_data = gzip.decompress(response.content) #gzipを解凍
            json_data = json.loads(decoded_data) #解凍したデータを格納
        except Exception as e:
            print("Error decoding gzip content:", e)
            json_data = response.json() #gzipの解凍にエラーが生じた場合は、レスポンス内容をそのままJSON形式で格納
    else:
        print("Response data is not gzip encoded.")
        json_data = response.json() #gzip形式でなければ、レスポンス内容をそのままJSON形式で格納
            
    #URLパラメータにおけるデータ形式の指定の仕方によって出力形式を分岐（GeoJSONかPBFか、response_formatの指定がないか）
    if "response_format" in mlit_params:
        response_format = mlit_params["response_format"].upper()
        if response_format == "GEOJSON":
            output_format = "GeoJSON" #パラメータで出力形式にGeoJSONが指定されていたらGeoJSONで出力する
        elif response_format == "PBF":
            print("Sorry, this program does not support PBF format. Output will be in GeoJSON format instead.")
            output_format = "GeoJSON" #パラメータで出力形式にPBFが指定されていたら、このプログラム上で対応できないので、代わりにGeoJSONで出力する
        else:
            print("Sorry, specified format is not supported in this program. Output will be in JSON format instead.")
            output_format = "JSON" #パラメータで不明な出力形式が指定されていたら、JSONで出力する
    else:
        output_format = input("Choose output format (JSON/CSV): ").upper() #response_formatの指定がないケースでは、出力形式をJSONかCSVで選択
    
    #選択した出力形式に応じて出力データの整形処理と保存を行う
    if output_format == "GeoJSON":
        with open("mlit_data_" + api_type + ".geojson", "w") as file:
            json.dump(json_data, file, indent=4, ensure_ascii=False) #JSON形式での文字列への変換。日本語の文字化け回避のためensure_ascii=Falseで対応。
        print("Data saved as GeoJSON successfully. Please see mlit_data_" + api_type + ".geojson")
    elif output_format == "CSV":
        headers = list(json_data["data"][0].keys()) #ヘッダー項目列の抽出
        with open("mlit_data_" + api_type + ".csv", "w", newline="", encoding="utf-8") as csvfile:
            csv_writer = csv.writer(csvfile)
            csv_writer.writerow(headers) #ヘッダー行を書き込む
            for item in json_data["data"]:
                csv_writer.writerow(item.values())
        print("Data saved as CSV successfully. Please see mlit_data_" + api_type + ".csv")
    else:
        with open("mlit_data_" + api_type + ".json", "w") as file:
            json.dump(json_data["data"], file, indent=4, ensure_ascii=False) #JSON形式での文字列への変換。日本語の文字化け回避のためensure_ascii=Falseで対応。
        print("Data saved as JSON successfully. Please see mlit_data_" + api_type + ".json")
#APIからの応答がエラーだった場合はその旨をコマンドラインに示す
else:
    print("Error:", response.status_code)

```

## 解説

- ほとんどの説明はスクリプト内のコメントで済むと思います。Pythonの外部モジュールとしてrequestsを使っているので、これは必要に応じてpipでインストールしてください
- スクリプトを実行すると、コマンドライン上でAPIキーを要求されますので、そこに自身のAPIキーを入力してください
- 最初の api_type = "XIT001" と mlit_params = {"year": "2015", "area": "12"} の部分はそれぞれ対象データの種類とそれに応じたパラメータなので、**ここを取得したいデータに応じて書き換えることで、このAPIが提供する様々なデータにアクセスできる**と思います
- 注意点として、パラメータのyearに"2024"など直近年を入れるとデータがないのでエラーが返されたりします。**パラメータの適正な範囲と、各データ種類における必須パラメータについては、公式マニュアル（[リンク](https://www.reinfolib.mlit.go.jp/help/apiManual/)） の記載に従ってください**
- このAPIの仕様で厄介に感じたのは、レスポンスがgzip圧縮形式となる場合がある点です。さらに、レスポンスのヘッダーを確認してgzipと判断したもののPythonでのgzip解凍エラーが生じたりしたので、その辺はtry構文や条件分岐にて、出力結果がうまく得られるように補正しています
- 出力結果はGeoJSONで得られるものやJSONで取得するものがあります。JSON形式のレスポンスに関してはCSVのほうが使いやすいケースもあると思うので、 **選択式でCSV形式の出力にも対応**しました
- PBFはバイナリファイルなので非対応としています。パラメータでPBFを出力形式に指定してもGeoJSONで出力します

## 出力結果の例

不動産価格（取引価格・成約価格）情報取得API（XIT001）で千葉県（area=12）の2015年データをCSVで取得し、EXCELに読み込ませた結果が下記です。
このデータには緯度経度など詳細な位置情報はなく、大まかな住所のみが含まれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122865/e3e898e2-9b91-3a15-405e-18c850bb938a.png)

また、位置座標値が含まれるデータはGeoJSON形式で出力することで、GIS（地理情報システム）でそのまま可視化できます
一例として、都市計画決定GISデータ（立地適正化計画区域）API（XKT003）で特定箇所のデータを取得し、QGISで読み込ませると下記のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122865/2bc03e10-e24d-5a8b-ef02-6b53fb86bf74.png)


# おわりに

以上、国土交通省の不動産情報ライブラリAPIからデータ取得するためのPythonスクリプトについて説明しました。
近年、国が整備した情報をオープンデータとしてサービス提供する流れが活発化しています。
これらをうまく活用することで、オーブンデータの活動をさらに加速していくことができると思うので、この記事が何らかの助けになれば幸いです。