e-StatのAPIを使ってデータを取得しCSVファイルに保存する方法

# この記事で共有したいこと
すでにe-StatのAPIに関する記事はいくつか投稿されているのですが、まだAPIなどに詳しくない身としては、どうすればいいのか分からない部分がありました。そこでこの記事では、私がGeminiを使いながら模索して見つけた「こうすればなんとか分析に使えるデータを用意できるやり方」を皆さんと共有したいと思います。

# 準備するもの
- e-Statのユーザー登録
- e-Statで作成したアプリケーションID

これらの紹介は[政府統計の総合窓口(e-Stat)のAPIを使ってみよう](https://qiita.com/mima_ita/items/44f358dc1bc4000d365d "e-Stat API")の記事冒頭にあります。用意するのに10分もかからないと思います。

加えて、e-Statで取得できるAPIのURLが必要です。

![スクリーンショット 2025-03-14 090543.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957867/f9363d1e-ed88-4af4-8c7f-706c93054c19.png)

![スクリーンショット 2025-03-14 090650.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957867/55454567-5152-4b52-8b77-1634c3f30239.png)

# Pythonモジュールの実装
ここからはGoogle Colabで実行した様子を紹介します。
モジュール実装のプログラムは[Pythonでe-Stat APIを使う](https://qiita.com/faux/items/efc4c8981510b78dd560 "Pythonでe-Stat APIを使う")の記事にあるものをそのまま使います。

![スクリーンショット 2025-03-14 091752.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957867/e20d3abb-1fa7-4578-a5f7-d16beee9f368.png)
まずはGoogle Drive上でプログラムを実行できるように準備します。過去の記事（[e-Statの活用例①](https://qiita.com/Sapolas/items/70c4a0e05438d9c57b10 "e-Statの活用例①")）で紹介しているの参考にしてみてください。

次のセルでモジュールを実装します。以下のプログラムをコピペして自身のアプリケーションIDを入力してください。
```python:sample.py
import urllib
import requests


class EstatRestApiClient:
    """
    This is a simple python module class for e-Stat API (ver.3.0).
    See more details at https://www.e-stat.go.jp/api/api-info/e-stat-manual3-0
    """

    def __init__(self, api_version=None, app_id=None):
        # base url
        self.base_url = "https://api.e-stat.go.jp/rest"

        # e-Stat REST API Version
        if api_version is None:
            self.api_version = "3.0"
        else:
            self.api_version = api_version

        # Application ID
        if app_id is None:
            self.app_id = "****************" # ここにアプリケーションIDをいれる
        else:
            self.app_id = app_id

    def getStatsList(self, params_dict, format="csv"):
        """
        2.1 統計表情報取得 (HTTP GET)
        """
        params_str = urllib.parse.urlencode(params_dict)
        if format == "xml":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/getStatsList?{params_str}"
            )
        elif format == "json":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/json/getStatsList?{params_str}"
            )
        elif format == "jsonp":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/jsonp/getStatsList?{params_str}"
            )
        elif format == "csv":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/getSimpleStatsList?{params_str}"
            )
        return url

    def getMetaInfoURL(self, params_dict, format="csv"):
        """
        2.2 メタ情報取得 (HTTP GET)
        """
        params_str = urllib.parse.urlencode(params_dict)
        if format == "xml":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/getMetaInfo?{params_str}"
            )
        elif format == "json":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/json/getMetaInfo?{params_str}"
            )
        elif format == "jsonp":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/jsonp/getMetaInfo?{params_str}"
            )
        elif format == "csv":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/getSimpleMetaInfo?{params_str}"
            )
        return url

    def getStatsDataURL(self, params_dict, format="csv"):
        """
        2.3 統計データ取得 (HTTP GET)
        """
        params_str = urllib.parse.urlencode(params_dict)
        if format == "xml":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/getStatsData?{params_str}"
            )
        elif format == "json":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/json/getStatsData?{params_str}"
            )
        elif format == "jsonp":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/jsonp/getStatsData?{params_str}"
            )
        elif format == "csv":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/getSimpleStatsData?{params_str}"
            )
        return url

    def postDatasetURL(self):
        """
        2.4 データセット登録 (HTTP POST)
        """
        url = (
            f"{self.base_url}/{self.api_version}"
            "/app/postDataset"
        )
        return url

    def refDataset(self, params_dict, format="xml"):
        """
        2.5 データセット参照 (HTTP GET)
        """
        params_str = urllib.parse.urlencode(params_dict)
        if format == "xml":
            url = (
                f"{self.base_url}/{self.api_version}"
                + f"/app/refDataset?{params_str}"
            )
        elif format == "json":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/json/refDataset?{params_str}"
            )
        elif format == "jsonp":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/jsonp/refDataset?{params_str}"
            )
        return url

    def getDataCatalogURL(self, params_dict, format="xml"):
        """
        2.6 データカタログ情報取得 (HTTP GET)
        """
        params_str = urllib.parse.urlencode(params_dict)
        if format == "xml":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/getDataCatalog?{params_str}"
            )
        elif format == "json":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/json/getDataCatalog?{params_str}"
            )
        elif format == "jsonp":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/jsonp/getDataCatalog?{params_str}"
            )
        return url

    def getStatsDatasURL(self, params_dict, format="xml"):
        """
        2.7 統計データ一括取得 (HTTP GET)
        """
        params_str = urllib.parse.urlencode(params_dict)
        if format == "xml":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/getStatsDatas?{params_str}"
            )
        elif format == "json":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/json/getStatsDatas?{params_str}"
            )
        elif format == "csv":
            url = (
                f"{self.base_url}/{self.api_version}"
                f"/app/getSimpleStatsDatas?{params_str}"
            )
        return url

```

![スクリーンショット 2025-03-14 092738.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957867/664f6d62-e0d3-4864-b3e7-90bd8842ebfd.png)

次のセルでデータをCSVファイルに保存するところまで行きます。
使うプログラムは以下のものです。
```python:sample
import requests
import csv
import io # 文字列をファイルのように扱うために使用

# EstatRestApiClientのインスタンスを作成
client = EstatRestApiClient(app_id='****************')# ここにアプリケーションIDをいれる

# パラメータを定義
# cntGetFlgを"Y"に設定
params = {
    "statsDataId": "0003005798",  # 統計データID
    "lang": "J",
    "metaGetFlg": "Y",
    "cntGetFlg": "Y",  # データコンテンツを取得するように設定
    "explanationGetFlg": "Y",
    "annotationGetFlg": "Y",
    "sectionHeaderFlg": "1",
    "replaceSpChars": "0",
    "appId": client.app_id # app_idをパラメータに追加
    # ... other parameters ...
}

# getStatsDataURLを使ってCSV形式でデータを取得するURLを取得
url = client.getStatsDataURL(params, format="csv")

# APIリクエストを送信
response = requests.get(url)

# レスポンスの確認
if response.status_code == 200:
    # レスポンスボディをCSVデータとして読み込む
    csv_data = response.content.decode('utf-8') # utf-8でデコード

    # ファイルに書き込む
    with open('estat_data.csv', 'w', encoding='utf-8', newline='') as f:
        writer = csv.writer(f)
        reader = csv.reader(csv_data.splitlines()) # splitlines()で文字列を行に分割
        for row in reader:
            writer.writerow(row)

    print("CSVファイル 'estat_data.csv' が保存されました。")
else:
    print(f"Error: {response.status_code}")
```

このプログラムの中に「パラメータ定義」があり、そこで先ほど取得したURLを使います。URLはコピーしたものをそのまま使うのではなく、
```:
http://api.e-stat.go.jp/rest/3.0/app/getSimpleStatsData?appId=&lang=J&statsDataId=0003005798&metaGetFlg=Y&cntGetFlg=N&explanationGetFlg=Y&annotationGetFlg=Y&sectionHeaderFlg=1&replaceSpChars=0
```
lang以降の文字について切り取りながら、パラメータ定義に書き込んでいきます。パラメータは&で区切られているので、それを頼りに上記の形式に合わせながら書き込んでください。

私が試したときはパラメータ定義の中にアプリケーションIDがないとエラーが出たため最後の方に書き加えています。

![スクリーンショット 2025-03-14 093927.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957867/4da9c25f-bc2e-4216-b43a-4cf885227fbf.png)

![スクリーンショット 2025-03-14 094023.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957867/fb725f56-66b9-4c21-92bf-232175ad15ce.png)

成功すればこのように、フォルダにCSVファイルが作成されます。
CSVファイルの中身は以下のようになります。

![スクリーンショット 2025-03-14 094219.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957867/d03f591e-5ae5-4f77-ac12-2a51bd26cc8d.png)

ここまでできればpandasを使って分析準備まで持っていけると思います。作成したCSVファイルを見ると、メタ情報が入っています。APIでデータを取得する際にメタ情報は取得しないようにすれば以降の操作が楽になるかもしれません。

# おわりに
今回は、使えるデータをAPIで取得すること優先に試したことを紹介しました。プログラムについてはGeminiに相談しながら書いたため間違っているところがあるかもしれません。間違いに気づいた方はコメントしてくださるととても助かります。
ここまで読んでくださりありがとうございました。