note.nkmk.me

Pythonでe-Stat APIを使って政府統計データを一括ダウンロード

Date: 2018-05-23 / tags: Python, pandas, スクレイピング, Web API

e-Statは日本の統計データを閲覧・ダウンロードできる政府統計ポータルサイト。国勢調査や人口推計、労働力調査など様々なデータが公開されている。

Webサイトで検索してデータをダウンロードできるが、その場合、形式がExcelに限られていたり、処理しにくいレイアウトだったりする。

e-StatではWeb APIが公開されていて、そちらを使うとシンプルなレイアウトでCSVで保存可能。一括ダウンロードも簡単。

公式にPythonのライブラリが公開されている。

が、ただデータをCSVでダウンロードするだけの場合はちょっと豪華すぎたので、シンプルなプログラムを作成した。内容と使い方を紹介する。

リポジトリはこちら。

pandasを使っているのでインストールが必要。

pip install pandas

以下の内容を説明する。

  • e-Stat APIの仕様と使い方
    • ユーザー登録・アプリケーションID取得
    • 仕様
    • 使い方
    • e-Stat APIで提供されているデータ
  • 統計表情報を取得しCSV・JSON・XMLで保存
    • 基本的な流れ
    • 関数化
  • 統計データを取得しCSV・JSON・XMLで保存
    • 基本的な流れ
    • 関数化
  • 全統計表情報をCSV保存しpandasで処理
  • 統計データを一括ダウンロード
    • 統計表情報のCSVをもとにダウンロード
    • 直接ダウンロード

市区町村別のデータが豊富なRESASのAPIを利用する方法は以下の記事を参照。

スポンサーリンク

e-Stat APIの仕様と使い方

ユーザー登録・アプリケーションID取得

e-Stat APIを使うには、ユーザー登録をしてアプリケーションIDを取得する必要がある。

ユーザー登録に必要なのはEメールアドレスだけ。

登録完了したらトップページからログイン。マイページAPI機能(アプリケーションID発行)でアプリケーションIDを取得する。

名称とURLを入力。名称は自由、Webサイトとして公開する予定が無ければURLはhttp://localhost/で問題ない。

発行ボタンをクリックするとアプリケーションIDが発行される。

仕様

仕様は以下。2018年5月現在の最新バージョンは2.1

使い方

データをダウンロードするだけであれば、ベースのURLにパラメータを表すクエリ文字列を付けたリクエストURLにアクセスするだけ(GETメソッド)。

例えば統計データをCSV形式で取得するURLは以下の通り。

https://api.e-stat.go.jp/rest/2.1/app/getSimpleStatsData?<パラメータ群(クエリ文字列)>

アプリケーションIDや取得したい統計表IDなどをクエリ文字列で指定する。

詳細は後述。

e-Stat APIで提供されているデータ

e-Stat APIで提供されているデータは以下。国勢調査や人口推計、家計調査などいろいろある。

統計表情報を取得しCSV・JSON・XMLで保存

統計データにはそれぞれ一意のIDである統計表IDが与えられている。統計表IDは統計表情報を取得することで確認できる。

統計データを取得するための準備として、まずは統計表情報をe-Stat APIを使って取得する。

基本的な流れ

統計表情報を取得するリクエストURLは仕様を参照。以下の通り。

  • JSON
    • https://api.e-stat.go.jp/rest/2.1/app/json/getStatsList?<パラメータ群(クエリ文字列)>
  • XML
    • https://api.e-stat.go.jp/rest/2.1/app/getStatsList?<パラメータ群(クエリ文字列)>

ここではCSVで保存する例について詳しく説明する。統計表情報は直接CSV形式で取得できないので、JSONで取得してからCSVに変換する。

パラメータは仕様を参照。以下の例ではstatsCode(政府統計コード)とsurveyYears(調査年月)を指定している。

JSONファイルに記述したパラメータを辞書として読み込み。

import urllib.request
import urllib.parse
import json
import pandas as pd
import pprint

with open('setting/get_stats_list_sample.json') as f:
    print(f.read())
# {
#     "statsCode": "00200524",
#     "surveyYears": "2017"
# }

with open('setting/get_stats_list_sample.json') as f:
    p = json.load(f)

print(p)
# {'statsCode': '00200524', 'surveyYears': '2017'}

print(type(p))
# <class 'dict'>

同様にアプリケーションIDも辞書として読み込んでupdate()メソッドで追加。正しいアプリケーションIDを記述したJSONファイルapp_id.jsonsettingフォルダに作成しておく。

with open('setting/app_id_sample.json') as f:
    print(f.read())
# {
#     "appId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# }

with open('setting/app_id.json') as f:
    p_id = json.load(f)

p.update(p_id)

urllib.parse.urlencode()で辞書をクエリ文字列に変換し、ベースURLと連結。

url = ('http://api.e-stat.go.jp/rest/2.1/app/json/getStatsList?'
       + urllib.parse.urlencode(p))

URLを開きデータを取得。取得できるデータはバイト列(bytes型)。

バイト列を文字列にデコードしてjson.loads()で辞書に変換。Python3.6からはjson.loads()に直接バイト列を渡せるようになったので、デコードは不要。

with urllib.request.urlopen(url) as response:
    data = response.read()

print(type(data))
# <class 'bytes'>

d = json.loads(data.decode())

pprintで辞書の上位階層のみ表示。仕様通りの構造のデータになっている。

pprint.pprint(d, depth=3)
# {'GET_STATS_LIST': {'DATALIST_INF': {'NUMBER': 21,
#                                      'RESULT_INF': {...},
#                                      'TABLE_INF': [...]},
#                     'PARAMETER': {'DATA_FORMAT': 'J',
#                                   'LANG': 'J',
#                                   'STATS_CODE': '00200524',
#                                   'SURVEY_YEARS': 2017},
#                     'RESULT': {'DATE': '2018-05-24T19:38:42.172+09:00',
#                                'ERROR_MSG': '正常に終了しました。',
#                                'STATUS': 0}}}

統計表データの本体であるTABLE_INFを取得。統計表IDや政府統計名などの情報を含む辞書を要素とするリストになっている。

リストの要素(辞書)一つが統計表一つに対応する。例ではリストの要素数が21になっているので、指定したパラメータに一致する統計表が21個あるという意味。

table_info_list = d['GET_STATS_LIST']['DATALIST_INF']['TABLE_INF']

print(type(table_info_list))
# <class 'list'>

print(len(table_info_list))
# 21

print(type(table_info_list[0]))
# <class 'dict'>

pprint.pprint(table_info_list[0])
# {'@id': '0003215840',
#  'CYCLE': '-',
#  'GOV_ORG': {'$': '総務省', '@code': '00200'},
#  'MAIN_CATEGORY': {'$': '人口・世帯', '@code': '02'},
#  'OPEN_DATE': '2018-04-13',
#  'OVERALL_TOTAL_NUMBER': 816,
#  'SMALL_AREA': 0,
#  'STATISTICS_NAME': '人口推計 平成29年10月1日現在人口推計',
#  'STATISTICS_NAME_SPEC': {'TABULATION_CATEGORY': '人口推計',
#                           'TABULATION_SUB_CATEGORY1': '平成29年10月1日現在人口推計'},
#  'STAT_NAME': {'$': '人口推計', '@code': '00200524'},
#  'SUB_CATEGORY': {'$': '人口', '@code': '01'},
#  'SURVEY_DATE': 201710,
#  'TITLE': {'$': '年齢(各歳),男女別人口及び人口性比-総人口,日本人人口', '@no': '001'},
#  'TITLE_SPEC': {'TABLE_NAME': '年齢(各歳),男女別人口及び人口性比-総人口,日本人人口'},
#  'UPDATED_DATE': '2018-04-14'}

この辞書のリストをjson_normalize()pandas.DataFrameに変換。辞書のキーが列名となる。

json_normalize()はデフォルトでは入れ子になった辞書のキーを.で接続して列名とするが、query()メソッドで指定できるように引数sepでアンダースコア_に変更している。同様の理由で、列名に含まれている@$も置換している。

df = pd.io.json.json_normalize(table_info_list, sep='_')

print(df.columns)
# Index(['@id', 'CYCLE', 'GOV_ORG_$', 'GOV_ORG_@code', 'MAIN_CATEGORY_$',
#        'MAIN_CATEGORY_@code', 'OPEN_DATE', 'OVERALL_TOTAL_NUMBER',
#        'SMALL_AREA', 'STATISTICS_NAME',
#        'STATISTICS_NAME_SPEC_TABULATION_CATEGORY',
#        'STATISTICS_NAME_SPEC_TABULATION_SUB_CATEGORY1', 'STAT_NAME_$',
#        'STAT_NAME_@code', 'SUB_CATEGORY_$', 'SUB_CATEGORY_@code',
#        'SURVEY_DATE', 'TITLE_$', 'TITLE_@no', 'TITLE_SPEC_TABLE_NAME',
#        'UPDATED_DATE'],
#       dtype='object')

df.columns = [s.replace('@', '').replace('$', 'val') for s in df.columns]
print(df.columns)
# Index(['id', 'CYCLE', 'GOV_ORG_val', 'GOV_ORG_code', 'MAIN_CATEGORY_val',
#        'MAIN_CATEGORY_code', 'OPEN_DATE', 'OVERALL_TOTAL_NUMBER', 'SMALL_AREA',
#        'STATISTICS_NAME', 'STATISTICS_NAME_SPEC_TABULATION_CATEGORY',
#        'STATISTICS_NAME_SPEC_TABULATION_SUB_CATEGORY1', 'STAT_NAME_val',
#        'STAT_NAME_code', 'SUB_CATEGORY_val', 'SUB_CATEGORY_code',
#        'SURVEY_DATE', 'TITLE_val', 'TITLE_no', 'TITLE_SPEC_TABLE_NAME',
#        'UPDATED_DATE'],
#       dtype='object')

このpandas.DataFrameをcsvファイルとして保存。

df.to_csv('download/stats_list_{}_{}.csv'.format(p['statsCode'], p["surveyYears"]), index=False)

以下のようなデータが保存される。

関数化

以上の流れを関数化。

import json
import os
import time
import urllib.parse
import urllib.request

import pandas as pd


def get_api_return_val(param, url):
    url += urllib.parse.urlencode(param)
    with urllib.request.urlopen(url) as response:
        return response.read()


def get_list(param, form='json'):
    if form == 'xml':
        url = 'http://api.e-stat.go.jp/rest/2.1/app/getStatsList?'
    else:  # json
        url = 'http://api.e-stat.go.jp/rest/2.1/app/json/getStatsList?'
    return get_api_return_val(param, url).decode()


def get_list_table_inf(param):
    d = json.loads(get_list(param, 'json'))
    return d['GET_STATS_LIST']['DATALIST_INF']['TABLE_INF']


def save_list(param, dir_path='.', filename='stats_list', form='csv',
              replace=True, sep='_', atmark='', dollar='val', **kwargs):
    path = os.path.join(dir_path, filename + '.' + form)

    if form == 'csv':
        l = get_list_table_inf(param)
        if replace:
            df = pd.io.json.json_normalize(l, sep=sep)
            df.columns = [s.replace('@', atmark).replace('$', dollar)
                          for s in df.columns]
            df.to_csv(path, index=False)
        else:
            pd.io.json.json_normalize(l).to_csv(path, index=False)
    elif form == 'json':
        d = json.loads(get_list(param, 'json'))
        with open(path, 'w') as f:
            json.dump(d, f, **kwargs)
    else:  # xml
        with open(path, 'w') as f:
            f.write(get_list(param, form))

JSON, XMLでも保存できるようにしている。

JSONはjson.loads()で辞書として読み込んでからjson.dump()でファイルに書き出している。インデントを指定する引数indentやUnicodeエスケープするかどうかを指定する引数ensure_asciiなどは可変長引数**kwargsを通して渡す。

XMLは取得したバイト列のデータを文字列にデコードしてそのまま保存するだけ。

以下のように使う。

関数化したコードをe_stat_utils.pyとして保存、同じディレクトリにおいてインポートして使っている。

import json
import pandas as pd
import e_stat_utils

dir_path = 'download'

with open('setting/app_id.json') as f:
    p_id = json.load(f)

with open('setting/get_stats_list_sample.json') as f:
    p_list = json.load(f)

p_list.update(p_id)
filename = 'stats_list_{}_{}'.format(p_list['statsCode'], p_list["surveyYears"])

e_stat_utils.save_list(p_list, dir_path, filename)
e_stat_utils.save_list(p_list, dir_path, filename, form='xml')
e_stat_utils.save_list(p_list, dir_path, filename, form='json',
                       ensure_ascii=False, indent=4)

JSON, XMLの場合、以下のようなデータが保存される。

統計データを取得しCSV・JSON・XMLで保存

統計表IDをもとに統計データをダウンロードする。流れは統計表情報の場合と同じ。

基本的な流れ

統計データを取得するリクエストURLは仕様を参照。以下の通り。

  • CSV
    • https://api.e-stat.go.jp/rest/2.1/app/getSimpleStatsData?<パラメータ群(クエリ文字列)>
  • JSON
    • https://api.e-stat.go.jp/rest/2.1/app/json/getStatsData?<パラメータ群(クエリ文字列)>
  • XML
    • https://api.e-stat.go.jp/rest/2.1/app/getStatsData?<パラメータ群(クエリ文字列)>

ここではCSV保存の例について説明する。統計データは直接CSVで取得できるので、統計表情報より簡単。

パラメータは仕様を参照。以下の例ではまずstatsDataId(統計表ID)のみを指定している。

リクエストURLを作成してバイト列のデータを取得するところまでは統計表情報の場合と同じ。

import urllib.request
import urllib.parse
import json

with open('setting/get_stats_data_sample.json') as f:
    print(f.read())
# {
#     "statsDataId": "0003215840"
# }

with open('setting/get_stats_data_sample.json') as f:
    p = json.load(f)

with open('setting/app_id.json') as f:
    p_id = json.load(f)

p.update(p_id)

url = ('http://api.e-stat.go.jp/rest/2.1/app/getSimpleStatsData?'
       + urllib.parse.urlencode(p))

with urllib.request.urlopen(url) as response:
    data = response.read()

print(type(data))
# <class 'bytes'>

getSimpleStatsDataではcsv形式のバイト列が取得できるので、それをdecode()メソッドで文字列にデコードしてそのまま保存するだけ。

with open('download/stats_data_{}_header.csv'.format(p['statsDataId']), 'w') as f:
    f.write(data.decode())

ヘッダーが不要な場合はパラメータsectionHeaderFlg2にする。

sectionHeaderFlg2としても1行目に余分な行が追加されるので、.split('\n', 1)[1]で削除して保存している。

.split('\n', 1)で文字列を最初の改行文字\nで分割しリスト化、[1]でその2つ目の要素を取得することで、先頭行を削除している。

p['sectionHeaderFlg'] = 2

url = ('http://api.e-stat.go.jp/rest/2.1/app/getSimpleStatsData?'
       + urllib.parse.urlencode(p))

with urllib.request.urlopen(url) as response:
    data = response.read()

with open('download/stats_data_{}.csv'.format(p['statsDataId']), 'w') as f:
    f.write(data.decode().split('\n', 1)[1])

それぞれ以下のようなデータが保存される。

関数化

以上の流れを関数化。

def get_data(param, form='csv'):
    if form == 'csv':
        url = 'http://api.e-stat.go.jp/rest/2.1/app/getSimpleStatsData?'
    elif form == 'xml':
        url = 'http://api.e-stat.go.jp/rest/2.1/app/getStatsData?'
    else:  # json
        url = 'http://api.e-stat.go.jp/rest/2.1/app/json/getStatsData?'
    return get_api_return_val(param, url).decode()

def save_data(param, stats_data_id=None, dir_path='.', filename=None,
              form='csv', section_header=False, skip=True, **kwargs):
    p = param.copy()
    if stats_data_id:
        p['statsDataId'] = stats_data_id

    if not filename:
        filename = p['statsDataId']

    path = os.path.join(dir_path, filename + '.' + form)

    if section_header:
        p['sectionHeaderFlg'] = 1
    else:
        p['sectionHeaderFlg'] = 2

    if os.path.exists(path) & skip:
        print('skip {} ({} already exists)'.format(p['statsDataId'], path))
    else:
        print('download {} to {}'.format(p['statsDataId'], path))
        data = get_data(p, form)
        if form == 'json':
            d = json.loads(data)
            with open(path, 'w') as f:
                json.dump(d, f, **kwargs)
        else:  # csv, xml
            if form == 'csv' and not section_header:
                data = data.split('\n', 1)[1]
            with open(path, 'w') as f:
                f.write(data)

引数の意味は以下の通り。

  • stats_data_id
    • 統計表ID
    • paramの値を上書きする
    • 省略した場合はparamの値が使われる
  • filename
    • 保存するファイル名
    • 省略した場合は統計表IDがファイル名となる
  • section_header
    • CSVで保存するときのみ有効
    • True: ヘッダー込みで保存
    • False: ヘッダーを除外して保存
  • skip
    • True: 同じファイル名のファイルがあったら保存しない
    • False: 同じファイル名のファイルがあったら上書き

paramで渡したパラメータ用の辞書を関数内で更新するので、最初にcopy()メソッドでコピーしている。コピーしておかないと、元のオブジェクト自体が更新されてしまう。

JSON, XMLでも保存できるようにしている。保存の方法は統計表情報の場合と同じ。

以下のように使う。

関数化したコードをe_stat_utils.pyとして保存、同じディレクトリにおいてインポートして使っている。

with open('setting/get_stats_data_sample.json') as f:
    p_data = json.load(f)

p_data.update(p_id)

e_stat_utils.save_data(p_data, dir_path=dir_path)
e_stat_utils.save_data(p_data, dir_path=dir_path)
e_stat_utils.save_data(p_data, dir_path=dir_path, skip=False)
e_stat_utils.save_data(p_data, '0003215841', dir_path=dir_path)
e_stat_utils.save_data(p_data, dir_path=dir_path, filename='header_true', section_header=True)
# download 0003215840 to download/0003215840.csv
# skip 0003215840 (download/0003215840.csv already exists)
# download 0003215840 to download/0003215840.csv
# download 0003215841 to download/0003215841.csv
# download 0003215840 to download/header_true.csv

e_stat_utils.save_data(p_data, dir_path=dir_path, form='xml')
e_stat_utils.save_data(p_data, dir_path=dir_path, form='json',
                       ensure_ascii=False, indent=4)
# download 0003215840 to download/0003215840.xml
# download 0003215840 to download/0003215840.json

JSON, XMLの場合、以下のようなデータが保存される。

全統計表情報をCSV保存しpandasで処理

統計表情報を取得する際のパラメータとしてアプリケーションIDのみを指定すると、提供されているすべての統計表情報が取得できる。

ローカルに全統計表情報があるとどんなデータが何件くらいあるかなどを確認するのに便利。

以下のように全統計表情報をCSV保存する。上で示した関数化したコードをe_stat_utils.pyとして保存、同じディレクトリにおいてインポートして使っている。

import json
import e_stat_utils

with open('setting/app_id.json') as f:
    p_id = json.load(f)

e_stat_utils.save_list(p_id, 'download', 'all_stats_list')

データ量が多いので4分ほどかかる。70MB程度のcsvファイルが作成される。

全統計表情報のcsvファイルをpandasで処理する例を示す。

csvを読み込み。引数dtype=strですべての列を文字列として読み込んでいる。これを指定しないと列のデータ型dtypeが自動的に選択され、たとえば文字列として扱うべき統計表ID0003215840が整数3215840として扱われたりしてしまう。

import pandas as pd

df = pd.read_csv('download/all_stats_list.csv', dtype=str)

全体の行数・列数と列名を確認。行数はデータ件数に相当する。

print(df.shape)
# (115716, 30)

print(df.columns)
# Index(['id', 'CYCLE', 'GOV_ORG_val', 'GOV_ORG_code', 'MAIN_CATEGORY_val',
#        'MAIN_CATEGORY_code', 'OPEN_DATE', 'OVERALL_TOTAL_NUMBER', 'SMALL_AREA',
#        'STATISTICS_NAME', 'STATISTICS_NAME_SPEC_TABULATION_CATEGORY',
#        'STATISTICS_NAME_SPEC_TABULATION_SUB_CATEGORY1',
#        'STATISTICS_NAME_SPEC_TABULATION_SUB_CATEGORY2',
#        'STATISTICS_NAME_SPEC_TABULATION_SUB_CATEGORY3',
#        'STATISTICS_NAME_SPEC_TABULATION_SUB_CATEGORY4',
#        'STATISTICS_NAME_SPEC_TABULATION_SUB_CATEGORY5', 'STAT_NAME_val',
#        'STAT_NAME_code', 'SUB_CATEGORY_val', 'SUB_CATEGORY_code',
#        'SURVEY_DATE', 'TITLE', 'TITLE_val', 'TITLE_no',
#        'TITLE_SPEC_TABLE_CATEGORY', 'TITLE_SPEC_TABLE_NAME',
#        'TITLE_SPEC_TABLE_SUB_CATEGORY1', 'TITLE_SPEC_TABLE_SUB_CATEGORY2',
#        'TITLE_SPEC_TABLE_SUB_CATEGORY3', 'UPDATED_DATE'],
#       dtype='object')

条件を指定して行数(データ件数)を確認。ブールインデックスまたはquery()を使う。

print(len(df[df['GOV_ORG_val'] == '総務省']))
# 38738

print(len(df.query('GOV_ORG_val == "総務省"')))
# 38738

文字列メソッド(str.xxx())で条件指定。numexprをインストールしている場合は、query()で文字列メソッドを指定する際に引数engine='python'が必要なので注意。

また、列に欠損値NaNが含まれている場合は引数naを指定する必要があるが、query()では指定できない。

print(len(df[df['SURVEY_DATE'].str.startswith('2017')]))
# 273

print(len(df.query('SURVEY_DATE.str.startswith("2017")', engine='python')))
# 273

print(len(df[df['TITLE_val'].str.contains('所得', na=False)]))
# 3643

統計表の作成機関名と作成機関コードの列を選択して重複行を削除、ソート。作成機関名と作成機関コードの対応が確認できる。

print(df[['GOV_ORG_val', 'GOV_ORG_code']].drop_duplicates().sort_values(by='GOV_ORG_code'))
#        GOV_ORG_val GOV_ORG_code
# 0              内閣府        00100
# 1591           警察庁        00130
# 1603           総務省        00200
# 40341          法務省        00250
# 40423          財務省        00350
# 40445          国税庁        00351
# 40501        文部科学省        00400
# 51856        厚生労働省        00450
# 65005        農林水産省        00500
# 93920        経済産業省        00550
# 111137    資源エネルギー庁        00551
# 111252       国土交通省        00600

統計表IDからその他の情報を確認したい場合は、IDの列をindexに指定すると便利。

df_id = df.set_index('id')

print(df_id.loc['0003215840', 'TITLE_val'])
# 年齢(各歳),男女別人口及び人口性比-総人口,日本人人口

print(df_id.loc['0003215840'])
# CYCLE                                                                       -
# GOV_ORG_val                                                               総務省
# GOV_ORG_code                                                            00200
# MAIN_CATEGORY_val                                                       人口・世帯
# MAIN_CATEGORY_code                                                         02
# OPEN_DATE                                                          2018-04-13
# OVERALL_TOTAL_NUMBER                                                      816
# SMALL_AREA                                                                  0
# STATISTICS_NAME                                         人口推計 平成29年10月1日現在人口推計
# STATISTICS_NAME_SPEC_TABULATION_CATEGORY                                 人口推計
# STATISTICS_NAME_SPEC_TABULATION_SUB_CATEGORY1                平成29年10月1日現在人口推計
# STATISTICS_NAME_SPEC_TABULATION_SUB_CATEGORY2                             NaN
# STATISTICS_NAME_SPEC_TABULATION_SUB_CATEGORY3                             NaN
# STATISTICS_NAME_SPEC_TABULATION_SUB_CATEGORY4                             NaN
# STATISTICS_NAME_SPEC_TABULATION_SUB_CATEGORY5                             NaN
# STAT_NAME_val                                                            人口推計
# STAT_NAME_code                                                       00200524
# SUB_CATEGORY_val                                                           人口
# SUB_CATEGORY_code                                                          01
# SURVEY_DATE                                                            201710
# TITLE                                                                     NaN
# TITLE_val                                        年齢(各歳),男女別人口及び人口性比-総人口,日本人人口
# TITLE_no                                                                  001
# TITLE_SPEC_TABLE_CATEGORY                                                 NaN
# TITLE_SPEC_TABLE_NAME                            年齢(各歳),男女別人口及び人口性比-総人口,日本人人口
# TITLE_SPEC_TABLE_SUB_CATEGORY1                                            NaN
# TITLE_SPEC_TABLE_SUB_CATEGORY2                                            NaN
# TITLE_SPEC_TABLE_SUB_CATEGORY3                                            NaN
# UPDATED_DATE                                                       2018-04-14
# Name: 0003215840, dtype: object

複数条件で行を抽出。query()を使ってもいいが、この場合あまり簡潔にならない。

df_population_2017 = df[(df['STAT_NAME_val'] == '人口推計')
                        & (df['SURVEY_DATE'].str.startswith('2017'))]

# df_population_2017 = df.query('STAT_NAME_val == "人口推計" & SURVEY_DATE.str.startswith("2017")', 
#                               engine='python')

print(len(df_population_2017))
# 21

print(df_population_2017[['id', 'TITLE_val']])
#                id                        TITLE_val
# 20806  0003215840     年齢(各歳),男女別人口及び人口性比-総人口,日本人人口
# 20807  0003215841       年齢(5歳階級),男女,月別人口-総人口,日本人人口
# 20808  0003215842           年齢(5歳階級),男女別人口及び割合-総人口
# 20809  0003215843       都道府県,男女別人口及び人口性比-総人口,日本人人口
# 20810  0003215844             都道府県,男女別人口-総人口,日本人人口
# 20811  0003215845                   都道府県別人口の割合-総人口
# 20812  0003215846                   都道府県別人口増減率-総人口
# 20813  0003215847                   都道府県別自然増減率-総人口
# 20814  0003215848                   都道府県別社会増減率-総人口
# 20815  0003215849    都道府県,年齢(5歳階級),男女別人口-総人口,日本人人口
# 20816  0003215850     都道府県,年齢(3区分),男女別人口-総人口,日本人人口
# 20817  0003215851  都道府県,年齢(3区分),男女別人口の割合-総人口,日本人人口
# 20818  0003215852               都道府県,男女別年齢構造指数-総人口
# 20819  0003215863    参考表 男女別人口の計算表-総人口,日本人人口,外国人人口
# 20820  0003215864   参考表 年齢(各歳),男女別人口の計算表-総人口,日本人人口
# 20821  0003215865     参考表 年齢(5歳階級),男女別死亡者数-日本人,外国人
# 20822  0003215866    参考表 年齢(5歳階級),男女別出入国者数-日本人,外国人
# 20823  0003215867     参考表 都道府県,男女別人口の計算表-総人口,日本人人口
# 20824  0003215868   参考表 都道府県,男女別出生児数及び死亡者数-日本人,外国人
# 20825  0003215869   参考表 都道府県,男女別都道府県間転出入者数-日本人,外国人
# 20826  0003215870        参考表 都道府県,男女別出入国者数-日本人,外国人

values属性で列のリストをNumPy配列numpy.ndarrayとして取得できる。

ids = df_population_2017['id'].values

print(ids)
# ['0003215840' '0003215841' '0003215842' '0003215843' '0003215844'
#  '0003215845' '0003215846' '0003215847' '0003215848' '0003215849'
#  '0003215850' '0003215851' '0003215852' '0003215863' '0003215864'
#  '0003215865' '0003215866' '0003215867' '0003215868' '0003215869'
#  '0003215870']

print(type(ids))
# <class 'numpy.ndarray'>

このリストを使って統計データを一括ダウンロードする方法を次に示す。

統計データを一括ダウンロード

統計表情報のCSVをもとにダウンロード

統計データを一括ダウンロードする関数を示す。

def save_data_multi(param, ids, dir_path='.', filename_words=None, sep='_',
                    form='csv', section_header=False, skip=True,
                    interval_time_sec=1, **kwargs):
    if filename_words:
        for i, *words in zip(ids, *filename_words):
            time.sleep(interval_time_sec)
            save_data(param, i, dir_path,
                      sep.join([str(word) for word in words]),
                      form, section_header, skip, **kwargs)
    else:
        for i in ids:
            time.sleep(interval_time_sec)
            save_data(param, i, dir_path, i,
                      form, section_header, skip, **kwargs)

基本的には統計表IDのリストをforループでまわし、上で定義した統計データをダウンロードする関数を呼んでいるだけ。

標準ライブラリのtimeモジュールをインポートしてtime.sleep()でダウンロードの間にインターバルを置いている(デフォルトは1秒)。

ファイル名を指定したい場合は、filename_wordsにリストのリストを指定する。リストの要素が順にsepを区切り文字として連結されファイル名となる。

デフォルトでは統計表IDがファイル名になる。

以下のように使う。

関数化したコードをe_stat_utils.pyとして保存、同じディレクトリにおいてインポートして使っている。

ids = ['0003215842', '0003215843', '0003215844']

e_stat_utils.save_data_multi(p_id, ids, dir_path)
# download 0003215842 to download/0003215842.csv
# download 0003215843 to download/0003215843.csv
# download 0003215844 to download/0003215844.csv

e_stat_utils.save_data_multi(p_id, ids, dir_path, form='xml')
# download 0003215842 to download/0003215842.xml
# download 0003215843 to download/0003215843.xml
# download 0003215844 to download/0003215844.xml

prefixes = ['0', '1', '2']
suffixes = ['xxx', 'yyy', 'zzz']

e_stat_utils.save_data_multi(p_id, ids, dir_path, [prefixes, ids, suffixes])
# download 0003215842 to download/0_0003215842_xxx.csv
# download 0003215843 to download/1_0003215843_yyy.csv
# download 0003215844 to download/2_0003215844_zzz.csv

統計表情報のcsvからpandasで条件をもとに抽出、その情報に従ってダウンロード。

df = pd.read_csv('download/all_stats_list.csv', dtype=str)

df_target = df[(df['STAT_NAME_val'] == '人口推計')
               & (df['SURVEY_DATE'].str.startswith('2017'))
               & (df['TITLE_val'].str.contains('出入国'))]

e_stat_utils.save_data_multi(p_id, df_target['id'].values, dir_path)
# download 0003215866 to download/0003215866.csv
# download 0003215870 to download/0003215870.csv

e_stat_utils.save_data_multi(p_id, df_target['id'].values, dir_path,
                             [df_target['GOV_ORG_code'].values,
                              df_target['id'].values,
                              df_target['TITLE_val'].values])
# download 0003215866 to download/00200_0003215866_参考表 年齢(5歳階級),男女別出入国者数-日本人,外国人.csv
# download 0003215870 to download/00200_0003215870_参考表 都道府県,男女別出入国者数-日本人,外国人.csv

直接ダウンロード

パラメータで指定して取得した統計表情報のすべての統計データを直接ダウンロードすることも可能。

統計表情報で取得できる辞書のリストから統計表IDのリストを抽出する。

def save_data_multi_direct(param_list, param_data, param_id=None,
                           dir_path='.', filename_words=None, sep='_',
                           form='csv', section_header=False, skip=True,
                           interval_time_sec=1, **kwargs):
    p_list = param_list.copy()
    p_data = param_data.copy()

    if param_id:
        p_list.update(param_id)
        p_data.update(param_id)

    l = get_list_table_inf(p_list)
    ids = [d['@id'] for d in l]

    if filename_words:
        fws = []
        keys = l[0].keys()
        n = len(ids)
        for word in filename_words:
            if type(word) == list:
                if word[0] in keys:
                    fws.append([d[word[0]].get(word[1]) for d in l])
                else:
                    fws.append([word[0]] * n)
            elif word in keys:
                fws.append([d[word] for d in l])
            else:
                fws.append([word] * n)

    save_data_multi(p_data, ids, dir_path, fws, sep,
                    form, section_header, skip, interval_time_sec, **kwargs)

ファイル名を制御したい場合は、filename_wordsにリストのリストを指定する。リストの要素が順にsepを区切り文字として連結されファイル名となる。

統計表情報の辞書のキーを指定するとその値をファイル名として使う。

統計表一つのデータに対応する辞書の中身は以下の通り。

# {'@id': '0003215840',
#  'CYCLE': '-',
#  'GOV_ORG': {'$': '総務省', '@code': '00200'},
#  'MAIN_CATEGORY': {'$': '人口・世帯', '@code': '02'},
#  'OPEN_DATE': '2018-04-13',
#  'OVERALL_TOTAL_NUMBER': 816,
#  'SMALL_AREA': 0,
#  'STATISTICS_NAME': '人口推計 平成29年10月1日現在人口推計',
#  'STATISTICS_NAME_SPEC': {'TABULATION_CATEGORY': '人口推計',
#                           'TABULATION_SUB_CATEGORY1': '平成29年10月1日現在人口推計'},
#  'STAT_NAME': {'$': '人口推計', '@code': '00200524'},
#  'SUB_CATEGORY': {'$': '人口', '@code': '01'},
#  'SURVEY_DATE': 201710,
#  'TITLE': {'$': '年齢(各歳),男女別人口及び人口性比-総人口,日本人人口', '@no': '001'},
#  'TITLE_SPEC': {'TABLE_NAME': '年齢(各歳),男女別人口及び人口性比-総人口,日本人人口'},
#  'UPDATED_DATE': '2018-04-14'}

入れ子になった辞書はリストでキーを指定。辞書のキーにない文字列はそのまま使われる。

以下のように使う。

関数化したコードをe_stat_utils.pyとして保存、同じディレクトリにおいてインポートして使っている。

e_stat_utils.save_data_multi_direct(p_list, p_data, p_id, dir_path,
                                    ['xxx', '@id', 'SURVEY_DATE', ['STAT_NAME', '$']])
# download 0003215840 to download/xxx_0003215840_201710_人口推計.csv
# download 0003215841 to download/xxx_0003215841_201710_人口推計.csv
# download 0003215842 to download/xxx_0003215842_201710_人口推計.csv
# download 0003215843 to download/xxx_0003215843_201710_人口推計.csv
# download 0003215844 to download/xxx_0003215844_201710_人口推計.csv
# download 0003215845 to download/xxx_0003215845_201710_人口推計.csv
# download 0003215846 to download/xxx_0003215846_201710_人口推計.csv
# download 0003215847 to download/xxx_0003215847_201710_人口推計.csv
# download 0003215848 to download/xxx_0003215848_201710_人口推計.csv
# download 0003215849 to download/xxx_0003215849_201710_人口推計.csv
# download 0003215850 to download/xxx_0003215850_201710_人口推計.csv
# download 0003215851 to download/xxx_0003215851_201710_人口推計.csv
# download 0003215852 to download/xxx_0003215852_201710_人口推計.csv
# download 0003215863 to download/xxx_0003215863_201710_人口推計.csv
# download 0003215864 to download/xxx_0003215864_201710_人口推計.csv
# download 0003215865 to download/xxx_0003215865_201710_人口推計.csv
# download 0003215866 to download/xxx_0003215866_201710_人口推計.csv
# download 0003215867 to download/xxx_0003215867_201710_人口推計.csv
# download 0003215868 to download/xxx_0003215868_201710_人口推計.csv
# download 0003215869 to download/xxx_0003215869_201710_人口推計.csv
# download 0003215870 to download/xxx_0003215870_201710_人口推計.csv

統計表情報取得のためのパラメータ指定によっては大量のデータをダウンロードしてしまうので注意。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事