====================================================
e-Stat API メタ情報読み取りノウハウ
myKINRIestatClaude00Flask_metaAPI読み取り知見ノウハウ.txt
作成: 2025-05 対象statsDataId: 0004006260(令和3年経済センサス)
====================================================
■ 背景・教訓
----------------------------------------------------------------------
e-Stat API の各統計表は、コード体系が統計ごとに全く異なる。
「cdArea='00000'」「cdTime='2021000000'」「cdTab='01'」のように
コードをハードコードで推測すると、STATUS=1(該当データなし)が返り
何も表示されない。
正しいアプローチ:
「まずメタ情報APIを叩いて、そこに書いてある値をそのまま使う」
■ STEP 1: メタ情報APIを叩く
----------------------------------------------------------------------
エンドポイント:
https://api.e-stat.go.jp/rest/3.0/app/json/getMetaInfo
必須パラメータ:
appId : アプリケーションID
statsDataId : 統計表ID(URLの sid= の値)
例:
GET https://api.e-stat.go.jp/rest/3.0/app/json/getMetaInfo
?appId=XXXX&statsDataId=0004006260
レスポンス構造:
d['GET_META_INFO']['METADATA_INF']['CLASS_INF']['CLASS_OBJ']
→ リスト。各要素が1つの「次元」に対応する。
各次元の構造:
{
'@id': 'tab', ← 次元ID(tab/cat01/cat02/cat03/area/time など)
'@name': '表章項目',
'CLASS': [ ← その次元のコード一覧(1件のときはdictになる点に注意)
{ '@code': '201-2021', '@name': '企業等数', '@level': '' },
{ '@code': '204-2021', '@name': '事業所数(海外支所を含む)', ... },
...
]
}
注意: CLASS が1件のとき list ではなく dict で返ってくる。
必ず以下のように正規化する:
items = obj['CLASS']
if isinstance(items, dict):
items = [items]
■ STEP 2: コードの罠に注意する
----------------------------------------------------------------------
推測で書いたコードと実際のコードが全然違うことがある。
実際に遭遇した例(statsDataId=0004006260):
次元 推測したコード 実際のコード
------- -------------- ----------------------
tab '01' '201-2021'(名称+年度が入った形式)
cat02 '1' '0'=総数, '1'=法人, '2'=個人
cat03 '0' '00'(2桁ゼロパディング)
cat01 level 1=大分類 '1'=特殊集計(AR/AB), '2'=大分類(A〜S), '4'=中分類
cdArea '00000' 実際は '00000' で合っていたが不要(単一値なら省略可)
cdTime '2021000000' 実際は '2021000000' で合っていたが不要(同上)
→ 単一値しか存在しない次元(area/time)は cdArea/cdTime を省略するのが安全。
絞り込まなくても同じデータが返る。
■ STEP 3: デバッグ用エンドポイントを必ず作る
----------------------------------------------------------------------
Flaskアプリに以下のルートを追加しておく:
@app.route('/api/meta_codes')
def api_meta_codes():
meta = get_meta()
result = {}
for k, items in meta.items():
result[k] = [{'code': i.get('@code'), 'name': i.get('@name'),
'level': i.get('@level')} for i in items]
return jsonify({'ok': True, 'meta': result})
アプリ起動後にブラウザで:
http://192.168.3.11:49002/api/meta_codes
を開けば、全次元・全コード・名称・levelが一目で確認できる。
このJSONを見てから app.py のコードを書く。
■ STEP 4: データAPIに渡すコードはメタから取得した値をそのまま使う
----------------------------------------------------------------------
NG(ハードコード推測):
params = {
'cdTab': '01',
'cdCat02': '1',
'cdCat03': '0',
'cdArea': '00000',
'cdTime': '2021000000',
}
OK(メタ駆動):
meta = get_meta()
tab_code = meta['tab'][0]['@code'] # '201-2021'
area_code = meta['area'][0]['@code'] # '00000'
time_code = meta['time'][0]['@code'] # '2021000000'
# → これをそのまま params に渡す
セレクトボックスもメタ駆動にする(Jinja2例):
{% for opt in tab_options %}
<option value="{{ opt.code }}">{{ opt.name }}</option>
{% endfor %}
# Python側:
tab_options = [{'code': i['@code'], 'name': i['@name']} for i in meta['tab']]
■ STEP 5: APIレスポンスのエラーを握り潰さない
----------------------------------------------------------------------
STATUS=0 が正常。STATUS=1 は「該当データなし」。
NG(エラーを握り潰す):
def parse_stats(raw):
try:
...
except (KeyError, TypeError):
return {} ← 何が起きたか分からない
OK(詳細を返す):
result_code = d['GET_STATS_DATA']['RESULT']['STATUS']
if str(result_code) != '0':
raise RuntimeError(
f"STATUS={result_code}: {d['GET_STATS_DATA']['RESULT']['ERROR_MSG']}\n"
f"パラメータ: {params}"
)
フロントエンドのステータスバーにエラー内容をそのまま表示すると、
どのパラメータが問題だったか一目で分かる。
■ STEP 6: level フィルタの注意点
----------------------------------------------------------------------
cat01(産業分類)の @level は統計によって全く異なる。
今回の例(0004006260):
@level='1' → AR(全産業), AB(農林漁業)など特殊集計コード
@level='2' → A農業林業, B漁業... など標準的な大分類
@level='4' → 01農業, 02林業... など中〜細分類
level でフィルタするときは:
filtered = [item for item in cat01_items if int(item.get('@level', 99)) <= level]
ただし @level が空文字の場合(tab次元など)は int() で例外になるので:
try:
lv = int(item.get('@level', 99))
except (ValueError, TypeError):
lv = 99
■ 汎用メタ取得関数テンプレート
----------------------------------------------------------------------
_meta_cache = None
def get_meta():
global _meta_cache
if _meta_cache is not None:
return _meta_cache
r = requests.get('https://api.e-stat.go.jp/rest/3.0/app/json/getMetaInfo',
params={'appId': API_KEY, 'statsDataId': STATS_ID},
timeout=30)
r.raise_for_status()
d = r.json()
status = d.get('GET_META_INFO', {}).get('RESULT', {}).get('STATUS', -1)
if str(status) != '0':
raise RuntimeError(f"MetaInfo APIエラー: {d['GET_META_INFO']['RESULT']}")
objs = d['GET_META_INFO']['METADATA_INF']['CLASS_INF']['CLASS_OBJ']
meta = {}
for obj in objs:
items = obj['CLASS']
if isinstance(items, dict):
items = [items] # ← 必須: 1件のときdictになる
meta[obj['@id']] = items
_meta_cache = meta
return meta
■ 別統計に応用するときのチェックリスト
----------------------------------------------------------------------
[ ] statsDataId を URL の sid= から取得
[ ] /api/meta_codes でコード一覧を確認
[ ] tab コードはメタから動的取得(ハードコードしない)
[ ] cat0N のコードはメタの @code をそのまま使う(ゼロパディングに注意)
[ ] @level の意味はその統計固有(1=大分類とは限らない)
[ ] 単一値の次元(area/time)はパラメータ省略でOK
[ ] STATUS != '0' を例外として投げ、フロントに表示する
[ ] CLASS が dict のとき list に正規化する処理を入れる
====================================================
[fedora myKINRIestatClaude05Flask]$ python3 - << 'EOF'
import requests, json
API_KEY = 'xxx'
r = requests.get('https://api.e-stat.go.jp/rest/3.0/app/json/getMetaInfo',
params={'appId': API_KEY, 'statsDataId': '0003376209'})
d = r.json()
objs = d['GET_META_INFO']['METADATA_INF']['CLASS_INF']['CLASS_OBJ']
for obj in objs:
items = obj['CLASS']
if isinstance(items, dict): items = [items]
print(f"\n=== {obj['@id']} ({obj['@name']}) ===")
for i in items[:5]:
print(f" code={i['@code']} level={i.get('@level','')} name={i['@name']}")
if len(items) > 5: print(f" ... (+{len(items)-5}件)")
EOF
=== tab (表章項目) ===
code=13 level= name=寄与度
code=14 level= name=前期比
code=15 level= name=前期比・年率
code=16 level= name=寄与度・年率
=== cat01 (国内総生産_実質季節調整系列) ===
code=100 level=1 name=国内総生産(支出側)
code=120 level=2 name=民間最終消費支出
code=130 level=3 name=民間最終消費支出_家計最終消費支出
code=150 level=4 name=民間最終消費支出_家計最終消費支出_除く持ち家の帰属家賃
code=180 level=2 name=民間住宅
... (+18件)
=== time (時間軸(四半期)) ===
code=1980000406 level=3 name=1980年4~6月期
code=1980000709 level=3 name=1980年7~9月期
code=1980001012 level=3 name=1980年10~12月期
code=1981000103 level=3 name=1981年1~3月期
code=1981000406 level=3 name=1981年4~6月期
... (+112件)
[fedora myKINRIestatClaude05Flask]$ nvim
app.py files.zip templates/

bitFlyer口座開設(ビットコイン1,000円もらえる)URL
