不動産情報APIをPythonで利用してみた

【更新履歴】
* 2024年5月10日：station（駅コード）を指定した不動産価格の取得が可能になっていたことに気がついたので，訂正しました．



# 概要

国土交通省が公開している不動産情報ライブラリAPIを利用して，Pythonで不動産価格を取得する方法を紹介します．Pythonを利用するよりも，webサイトからポチポチ選択してCSVファイルをダウンロードするのが楽かも:sweat_smile:？

https://www.reinfolib.mlit.go.jp/

:::note
**公式による説明**
不動産情報ライブラリとは、不動産の取引価格、地価公示等の価格情報や防災情報、都市計画情報、周辺施設情報等、不動産に関する情報をご覧になることができる国土交通省のWEBサイトです。
:::

# 1. 登録作業
APIを利用するには，利用申請をする必要があります．[利用申請のページ](https://www.reinfolib.mlit.go.jp/api/request/)から，氏名，メールアドレス，利用目的などの必要事項を記入して申請します．しばらく〜 数日するとAPIキーが発行されます．気長に待ちましょう．

基本的な使い方は，[API操作説明のページ](https://www.reinfolib.mlit.go.jp/help/apiManual/#titleApi2)に記載されています．**取得したい地域の不動産情報を入手するには，取引時期，都道府県コード，市区町村コードを指定すればよい**ことがわかります．

# 2. 都道府県コードの取得
都道府県コードは，[API操作説明のページ](https://www.reinfolib.mlit.go.jp/help/apiManual/#titleApi2)に，下記の表のような形でまとめられています．

|コード|	日本語表記|	英語表記|
|:---:|:-------:|:----------:|
|1	|北海道|	Hokkaido|
|2	|青森県|	Aomori Prefecture|
|3	|岩手県|	Iwate Prefecture|
|︙ |︙|︙|

辞書形式でまとめておくと利用しやすいので，**都道府県名から都道府県コードを対応させた辞書**を作成してみました．``pref2id['東京都']``という形で東京都のidになります．

~~~python
pref2id = {
'北海道': 1, '青森県': 2,'岩手県': 3, '宮城県': 4, '秋田県': 5, '山形県': 6, '福島県': 7, '茨城県': 8, '栃木県': 9, '群馬県': 10,
'埼玉県': 11, '千葉県': 12,'東京都': 13,'神奈川県': 14,'新潟県': 15,'富山県': 16,'石川県': 17, '福井県': 18,'山梨県': 19,'長野県': 20,
'岐阜県': 21,'静岡県': 22, '愛知県': 23, '三重県': 24, '滋賀県': 25, '京都府': 26, '大阪府': 27, '兵庫県': 28, '奈良県': 29, '和歌山県': 30,
'鳥取県': 31, '島根県': 32, '岡山県': 33, '広島県': 34, '山口県': 35, '徳島県': 36, '香川県': 37, '愛媛県': 38, '高知県': 39, '福岡県': 40,
'佐賀県': 41, '長崎県': 42, '熊本県': 43, '大分県': 44, '宮崎県': 45, '鹿児島県': 46, '沖縄県': 47}
~~~

# 3. 市区町村コードの取得
操作説明によると都市名の対応するコードを取得するには**都道府県内市区町村一覧取得API**を利用するようです．都道府県コードから含まれる市区町村の情報がすべて表示されるAPIとなります．

* URL: https://www.reinfolib.mlit.go.jp/ex-api/external/XIT002?<パラメータ>
* パラメータ
    * area: 都道府県コード
 
:::note
APIキーの値を "12345xxxxx"と表記しています．実際は，取得したAPIキーの値を利用してください．
:::

~~~python
import requests

url = "https://www.reinfolib.mlit.go.jp/ex-api/external/XIT002"
headers = {"Ocp-Apim-Subscription-Key": "12345xxxxx"}
params = {"area": pref2id["東京都"]}

response = requests.get(url, headers=headers, params=params)
json_data = response.json()

print(json_data)

# 出力例
# {'status': 'OK', 'data': [{'id': '13101', 'name': '千代田区'}, {'id': '13102', 'name': '中央区'}, {'id': '13103', 'name': '港区'}, {'id': '13104', 'name': '新宿区'}, {'id': '13105', 'name': '文京区'}, {'id': '13106', 'name': '台東区'}, {'id': '13107', 'name': '墨田区'}, {'id': '13108', 'name': '江東区'}, {'id': '13109', 'name': '品川区'}, {'id': '13110', 'name': '目黒区'}, {'id': '13111', 'name': '大田区'}, 
# {'id': '13112', 'name': '世田谷区'}, {'id': '13113', 'name': '渋谷区'}, {'id': '13114', 'name': '中野区'}, {'id': '13115', 'name': '杉並区'}, {'id': '13116', 'name': '豊島区'}, {'id': '13117', 'name': '北区'}, {'id': '13118', 'name': '荒川区'}, {'id': '13119', 'name': '板橋区'}, {'id': '13120', 'name': '練馬区'}, {'id': '13121', 'name': '足立区'}, {'id': '13122', 'name': '葛飾区'}, {'id': '13123', 'name': '江戸川区'}, 
# {'id': '13201', 'name': '八王子市'}, {'id': '13202', 'name': '立川市'}, {'id': '13203', 'name': '武蔵野市'}, {'id': '13204', 'name': '三鷹市'}, {'id': '13205', 'name': '青梅市'}, {'id': '13206', 'name': '府中市'}, {'id': '13207', 'name': '昭島市'}, {'id': '13208', 'name': '調布市'}, {'id': '13209', 'name': '町田市'}, {'id': '13210', 'name': '小金井市'}, {'id': '13211', 'name': '小平市'}, {'id': '13212', 'name': '日野市'}, 
# {'id': '13213', 'name': '東村山市'}, {'id': '13214', 'name': '国分寺市'}, {'id': '13215', 'name': '国立市'}, {'id': '13218', 'name': '福生市'}, {'id': '13219', 'name': '狛江市'}, {'id': '13220', 'name': '東大和市'}, {'id': '13221', 'name': '清瀬市'}, {'id': '13222', 'name': '東久留米市'}, {'id': '13223', 'name': '武蔵村山市'}, {'id': '13224', 'name': '多摩市'}, {'id': '13225', 'name': '稲城市'}, {'id': '13227', 'name': '羽村市'}, 
# {'id': '13228', 'name': 'あきる野市'}, {'id': '13229', 'name': '西東京市'}, {'id': '13303', 'name': '瑞穂町'}, {'id': '13305', 'name': '日の出町'}, {'id': '13307', 'name': '檜原村'}, {'id': '13308', 'name': '奥多摩町'}, {'id': '13361', 'name': '大島町'}, {'id': '13362', 'name': '利島村'}, {'id': '13363', 'name': '新島村'}, 
# {'id': '13364', 'name': '神津島村'}, {'id': '13381', 'name': '三宅村'}, {'id': '13382', 'name': '御蔵島村'}, {'id': '13401', 'name': '八丈町'}, 
# {'id': '13402', 'name': '青ヶ島村'}, {'id': '13421', 'name': '小笠原村'}]}

~~~
上記のコードのjson_dataから，所望の市区町村名とidの情報を抽出するための関数を作成してみましょう．

~~~python: json_dataと市区町村名からidを取得する関数
# json_dataと市区町村名からidを取得する関数
def search_city_id(json_data, city_name):
    # JSONデータから都市名を検索し、対応するIDを取得する
    for entry in json_data['data']:
        if entry['name'] == city_name:
            return entry['id']
    # 都市名が見つからない場合は「存在しません」を返す
    return f"{city_name}のidは存在しません"

id = search_city_id(json_data, "港区")
print(id)
# 13103
~~~

3までの内容で都道府県コード，市区町村コードを見つけることができます．取引時期を指定すれば，不動産価格情報を取得できることになります．

# 4. 不動産価格の取得

* URL = https://www.reinfolib.mlit.go.jp/ex-api/external/XIT001?＜パラメータ＞
* パラメータ
    * year: 取引時期（必須項目）
    * priceClassification: 価格情報区分コード 01: 不動産取引価格．02: 成約価格． 未指定：両方
    * quarter：取引時期（四半期）． 1 〜 4
    * area: 都道府県コード． 1 〜 47
    * city：市区町村コード
    * station：駅コード（コードの入手が難しい）

操作説明によると，**取得したい地域の不動産情報を入手するには，取引時期，都道府県コード，市区町村コードを指定すればよい**ことがわかります．次の例は，2023年，東京都，港区の不動産価格情報を取得したものとなります．


~~~python: 2023年東京都港区不動産情報
import requests

url = "https://www.reinfolib.mlit.go.jp/ex-api/external/XIT001" # 不動産価格
headers = {"Ocp-Apim-Subscription-Key": "12345xxxxx"}

area_code = pref2id["東京都"]
city_code = search_city_id(json_data, "港区")

params = {"year":2023,
          "area": area_code, 
          "city": city_code,
         }

response = requests.get(url, headers=headers, params=params)
json_response_data = response.json()
print(json_response_data)
# {'status': 'OK', 'data': [
# {'PriceCategory': '不動産取引価格情報', 'Type': '中古マンション等', 'Region': '', 'MunicipalityCode': '13103', 'Prefecture': '東京都', 'Municipality': '港区', 'DistrictName': '麻布台', 'TradePrice': '100000000', 'PricePerUnit': '', 'FloorPlan': '１ＬＤＫ', 'Area': '40', 'UnitPrice': '', 'LandShape': '', 'Frontage': '', 'TotalFloorArea': '', 'BuildingYear': '2014年', 'Structure': 'ＲＣ', 'Use': '住宅', 'Purpose': '住宅', 'Direction': '', 'Classification': '', 'Breadth': '', 'CityPlanning': '商業地域', 'CoverageRatio': '80', 'FloorAreaRatio': '500', 'Period': '2023年第1四半期', 'Renovation': '未改装', 'Remarks': ''}, 
# {'PriceCategory': '不動産取引価格情報', 'Type': '中古マンション等', 'Region': '', 'MunicipalityCode': '13103', 'Prefecture': '東京都', 'Municipality': '港区', 'DistrictName': '東新橋', 'TradePrice': '160000000', 'PricePerUnit': '', 'FloorPlan': '２ＬＤＫ', 'Area': '65', 'UnitPrice': '', 'LandShape': '', 'Frontage': '', 'TotalFloorArea': '', 'BuildingYear': '2002年', 'Structure': 'ＲＣ', 'Use': '住宅', 'Purpose': '', 'Direction': '', 
~~~
json_response_dataの値には，statusとdataがあり，dataの値が必要な情報であるので，json_response_data["data"]で抽出します．pandasを利用して，表示をきれいにしてみました．



~~~python
import pandas as pd

df = pd.DataFrame(json_response_data["data"])
print(df.head())

 	PriceCategory 	Type 	Region 	MunicipalityCode 	Prefecture 	Municipality 	DistrictName 	TradePrice 	PricePerUnit 	FloorPlan 	... 	Purpose 	Direction 	Classification 	Breadth 	CityPlanning 	CoverageRatio 	FloorAreaRatio 	Period 	Renovation 	Remarks
# 0 	不動産取引価格情報 	中古マンション等 		13103 	東京都 	港区 	麻布台 	100000000 		１ＬＤＫ 	... 	住宅 				商業地域 	80 	500 	2023年第1四半期 	未改装 	
# 1 	不動産取引価格情報 	中古マンション等 		13103 	東京都 	港区 	東新橋 	160000000 		２ＬＤＫ 	... 					商業地域 	80 	600 	2023年第1四半期 	改装済み 	
# 2 	不動産取引価格情報 	中古マンション等 		13103 	東京都 	港区 	元麻布 	360000000 		２ＬＤＫ 	... 	住宅 				第１種中高層住居専用地域 	60 	300 	2023年第1四半期 	改装済み 	
# 3 	不動産取引価格情報 	宅地(土地と建物) 	住宅地 	13103 	東京都 	港区 	元麻布 	2500000000 			... 	住宅 	北東 	区道 	4.3 	第１種中高層住居専用地域 	60 	300 	2023年第1四半期 		
# 4 	不動産取引価格情報 	宅地(土地) 	住宅地 	13103 	東京都 	港区 	元麻布 	540000000 	6500000 		... 	住宅 	南西 	区道 	5.4 	第１種中高層住居専用地域 	60 	300 	2023年第1四半期 	
~~~

stationの駅コードを指定すれば，area，cityを指定しなくても，駅周辺の情報を入手できるはずです．
例えば，横浜駅周辺を指定する場合は，
~~~python
params = {"year":2023,
          "station":"004633"}
~~~
。という具合にparamsを指定すれば，取得することが可能となります．~~しかし，駅コードの部分に不具合があるようで操作説明にあるN02_005gの駅データのグループコード値を入れても2024年4月20日時点では動作しませんでした．~~　何らかの変更があれば，記事を加筆修正する予定です．

駅コードの取得については、続きの記事

https://qiita.com/AzukiImo/items/89981c5af7c592ba1af8

を参照してもらえると幸いです．

# 5. その他
類似の内容やPythonラッパーがあるので，記載しておきます．

https://qiita.com/Guz9N9KLASTt/items/adbadc5fb7d72c05d1fe

https://qiita.com/OgawaHideyuki/items/a7b3df8bc6cffe3a6bdb