Ｐython 不動産ライブラリAPIを使ってみる コーディング編

行動変容アプリのコーディング編を書いていく。⇒ <a href="https://qiita.com/mm02060116/items/08f3dcc13e2084acf2ff">前回の記事はこちら</a>

目次
1.UIの作成
2.APIリクエストの作成
3.ディレクトリ構造の確認
4.次回（デプロイ編）

1.UIの作成
-
まず初めにUIについて考える。
UIとは、画面のボタンや文字などの操作部分であり、ユーザーが直感的に使いやすいものである必要がある。
不動産取引情報にAPIリクエストを送るために、以下のデータをユーザーから入力が必要。

【必要なユーザー入力】
・都道府県名
・市町村名
・検索したい取引年度

上記の3つのユーザー入力に必要な入力フィールドと、検索ボタンを1つ設置したシンプルなUIを考える。

```python:main.py
import streamlit as st
from src.send_api import connect_api
from src.pref_data import pref_id, change_index

# フッターのHTMLとCSS
footer = """
    <style>
    .footer {
        position: fixed;
        left: 0;
        bottom: 0;
        width: 100%;
        background-color: #f1f1f1;
        color: black;
        text-align: center;
        padding: 10px;
    }
    </style>
    <div class="footer">
        <p>不動産価格（取引価格・成約価格）情報取得API（国土交通省）を基に、作成しています。</p>
    </div>
    """

# フッターを挿入
st.markdown(footer, unsafe_allow_html=True)

# 画面に表示するアプリ名
st.title("不動産取引情報検索")

# 3つのユーザー入力用フィールドを設置（カラムで横並びに設定）
input_col1, input_col2, input_col3 = st.columns(3)
with input_col1:
    prefecture = st.selectbox(
        label="都道府県", options=[index for index, i in pref_id.items()]
    ) # pref_idについては、後ほど実装
with input_col2:
    city = st.text_input(label="市町村（空白可）", placeholder="大阪市")
with input_col3:
    year = st.number_input(label="取引時期（西暦）", value=2024, step=0)

# 検索ボタンを設置(connect_api関数は後ほど実装)
search = connect_api(api_key, prefecture, city, year) if st.button("検索") else None
```

2.APIリクエストの作成
-
前項のコードでは未定義のもの
pref_id
connect_api関数

【pref_idについて】
都道府県については47個しかないため、予めpref_idとしてpref_data.pyに辞書型データとして格納しておき、モジュールとして呼び出す仕様とした。

```python:pref_data.py
pref_id = {
    "北海道": "01",
    "青森県": "02",
    "岩手県": "03",
    "宮城県": "04",
    "秋田県": "05",
    "山形県": "06",
    "福島県": "07",
    "茨城県": "08",
    "栃木県": "09",
    "群馬県": "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",
}
```


【connect_api関数について】
都道府県についてはデータ数が少ないため、予めデータとして準備するのも手間ではないが、各都道府県の市町村コードを全て調べてデータ化するのは少し骨が折れる。
調べたところ、市町村コードを取得できるAPIが予め用意してあるため、APIリクエストする際に、ユーザーが入力した市町村コードを取得する仕様とした。
<a href="https://www.reinfolib.mlit.go.jp/help/apiManual/#titleApi5"> ⇒ 都道府県内市区町村一覧取得API</a>

```python:send_api.py
# APIによるデータ取得

# インポート
import streamlit as st
import requests
import pandas as pd
from src.pref_data import pref_id

# API接続関数
def connect_api(api_key: str | int, prefecture: str, city: str | None, year: int):
    api_key = api_key
    url = "https://www.reinfolib.mlit.go.jp/ex-api/external/XIT002"
    headers = {"Ocp-Apim-Subscription-Key": f"{api_key}"}
    params = {"year": year, "area": pref_id[prefecture]}
    response = requests.get(url, headers=headers, params=params)
    json_data = response.json()

    #  市町村コードの取得
    def send_city_id(json_data, city: str | None):
        try:
            for item in json_data["data"]:
                if item["name"] == city:
                    return item["id"]
            if city == "":
                return None
            else:
                return "該当する市町村が存在しません"
        except KeyError:
            return "該当する市町村が存在しません"

    #  入力された市町村が存在しない場合
    city_id = send_city_id(json_data, city)
    url = "https://www.reinfolib.mlit.go.jp/ex-api/external/XIT001"
    params["city"] = city_id
    response = requests.get(url, headers=headers, params=params)
    json_data = response.json()

    #  検索結果に応じた内容を画面描画
    try:
        df = pd.DataFrame(json_data["data"])
        df["TradePrice"] = df["TradePrice"].apply(lambda x: f"¥{int(x):,}")
        df = df.drop(
            columns=[
                "PriceCategory",
                "Prefecture",
                "MunicipalityCode",
                "UnitPrice",
                "Frontage",
                "PricePerUnit",
            ]
        )
        df = df.rename(columns=change_index)
        df.rename(
            columns={"用途地区": "取引価格", "取引価格": "用途地区"}, inplace=True
        )
        df["取引価格"], df["用途地区"] = df["用途地区"], df["取引価格"]
        st.link_button("空き家を登録してみる（外部サイト）", url=my_url)
        st.write(f"{year}年の{prefecture}{city}の取引情報")
        st.write(f"取引件数{str(len(df))}件")
        st.dataframe(df)

    except KeyError:
        st.write("該当する市町村が存在しません")
```
上記のコードでは未定義のもの

change_index
api_key

【change_indexについて】
APIリクエストで取得したデータが英語のため、和訳して表示することを考える。
データ数は20データ程しかないため、都道府県データと同じように、pref_data.pyに辞書型データとして格納し、change_indexモジュールとして呼び出す仕様とした。
```python:pref_data.py
# 下記を追記

change_index = {
    "PriceCategory": "情報種別",
    "Type": "取引種別",
    "Region": "用途地区",
    "Municipality": "市町村",
    "DistrictName": "地名",
    "TradePrice": "取引価格",
    "FloorPlan": "間取り",
    "Area": "面積（㎡）",
    "LandShape": "土地の形状",
    "TotalFloorArea": "延床面積（㎡）",
    "BuildingYear": "建築年",
    "Structure": "建物構造",
    "Use": "建物用途",
    "Purpose": "取引後の利用用途",
    "Direction": "前面道路の方位",
    "Classification": "前面道路の種類",
    "Breadth": "前面道路の幅員（m）",
    "CityPlanning": "都市計画区域",
    "CoverageRatio": "建蔽率（%）",
    "FloorAreaRatio": "容積率（%）",
    "Period": "取引時期",
    "Renovation": "リノベの有無",
    "Remarks": "備考"
}
```

【api_key】
connect_api関数にAPIキーを渡す必要があるが、大変機微な情報であるため、ハードコーディングはできない。
そのため秘匿化処理を含めて、次回のデプロイ編で解説する。

3.ディレクトリ構造の確認
-
現在のディレクトリ構造を確認してみる。
_ _init_ _.pyについては　⇒　<a href="https://qiita.com/ryoichiro009/items/446a4a647943addac351">Python __init__.pyってなに？</a>

```
my_project/
│
├── main.py
├── __init__.py
│
├── src/
│   ├── __init__.py
│   ├── pref_data.py
│   ├── send_api.py
│
```
4.次回（デプロイ編）
-
次回、Streamlitクラウドへデプロイした話を書く予定。