はじめに
e-StatのAPIを使って、外部からデータを引っ張るプログラムの紹介です。
外部データを引っ張れるようになるとかなり楽しいです!
今回はみんな大好きカレーパンの時系列売価を引っ張ってみました!
まだ触ったことのない方に向けて記載してみました!
この記事が役立つ方
- WebAPI初心者で
- 外部からデータを引っ張ってみたい
- Ruby初心者
この記事のメリット
- 外部データを引っ張る具体的な手順が分かるようになる
環境
- OS: macOS Mojave version 10.14.6
- シェル: zsh
- 使用言語: Ruby 2.6.3
- HTTPクライアントライブラリ: HTTPClient
- 使用するWeb API: e-Stat−API
- 取得するデータ:JSON形式
ディレクトリ構成
今回のディレクトリ構成は以下の通りです。
price_check_app/
├ conf/ ─ config.yml
├ src/ ─ Price.rb
│ └ PriceInfo.rb
├ test/ ─ test.rb
└ lib/
では、実際にプログラムを組んでみましょう。
まずは事前準備からです。
【準備 1】e-Statアカウント作成とアプリケーションID発行
利用ガイド | 政府統計の総合窓口(e-Stat)−API機能
上記リンクに従い、アカウント作成とアプリケーションIDの発行を実施してください。
API機能(アプリケーションID発行)で取得できます。
作成したアプリケーションを外部公開しない場合、入力するURLは http://localhost/ で大丈夫です。
【準備 2】e-StatのDBからどのデータを拾うかを決める
小売物価統計調査 小売物価統計調査(動向編) 主要品目の都市別小売価格-県庁所在市及び人口15万以上の市(2015年1月~) 月次 | データベース | 統計データを探す | 政府統計の総合窓口
今回はこれを使ってみようと思います。
ここからは欲しいデータを引っ張るために、いくつかIDなどをメモしておく必要があります。
1.統計表表示 ID: 0003105586
今回は大好きなカレーパンの物価推移を見てみることにします。
2.銘柄(H27年基準): 1023(カレーパン)
市区町村を探す | 政府統計の総合窓口
あとは上記リンクから、拾いたい都市名の地域コードもメモしましょう。
今回は東京都の特別区部のカレーパンの物価を見てみることにします。
拾うデータによって対応している地域がまちまちなので、欲しい地域が見当たらないかもしれません。
3.標準地域コード: 13100(東京都 特別区部)
【準備 3】HTTPClientのgemインストール
プログラムの下準備です。
今回はHTTPクライアントライブラリにHTTPClientを使用します。
$ gem install httpclient
なければ上記のコマンドでHTTPClientのgemをインストールして下さい。
【準備 4】出力されるJSONの構造理解のために必要なもの
JSON Formatter - Chrome ウェブストア
Chromeの拡張機能の紹介です。
JSONの可読性を高めるために、このJSON Formatterが大活躍でした。
出力データが配列 in ハッシュ in 配列 in ...みたいな内容なので、冷静に[]
と{}
を見ないと混乱します。
【Before】 JSON Formatterなし
「もうマヂ無理…」
【After】 JSON Formatter*あり*
「スッキリ!なるほどこういう構造ね!」
絶対入れておいた方がいいです。
次からソースコードの紹介に移ります。
【ソースコード】conf/config.yml
まずはconfig.yml
です。
可変式にするために抜き出しました。
ここのIDなどを変えることで、拾える情報が変わります。
#API取得先URL。appの後の「json」でファイル形式を指定
url : 'http://api.e-stat.go.jp/rest/2.1/app/json/getStatsData'
#e-Statから取得したアプリケーションID
appId : '00000000000000000000000000'
#統計表ID
statsDataId : '0003105586'
#銘柄(H27年基準)CD(5ケタ表記)
cdCat01 : '01023'
#標準地域コード
cdArea : '13100'
【ソースコード】src/Price.rb
次に本体です。
各コードの詳細はコメントをご確認ください、
require 'httpclient'
require 'resolv'
require 'json'
require 'yaml'
require './src/PriceInfo'
class Price
#JSON形式で出力されたときの階層別に変数定義しておく。拾わないものは定義不要。
#ルートパラメータ
@@GET_STATS_DATA = "GET_STATS_DATA"
#統計データの情報
@@STATISTICAL_DATA = "STATISTICAL_DATA"
#帳票情報
@@TABLE_INF = "TABLE_INF"
#大分類
@@STATISTICS_NAME = "STATISTICS_NAME"
#帳票名
@@TITLE = "TITLE"
#調査サイクル
@@CYCLE = "CYCLE"
#最終更新日
@@UPDATED_DATE = "UPDATED_DATE"
#クラス情報
@@CLASS_INF = "CLASS_INF"
@@CLASS_OBJ = "CLASS_OBJ"
#データ情報
@@DATA_INF = "DATA_INF"
@@VALUE = "VALUE"
#valueの中身
@@TIME = "@time"
#コンストラクタ
def initialize()
end
#メイン処理メソッド
def doProcess()
#API取得先URLの読み込み
url = YAML.load_file('./conf/config.yml')["url"]
#e-Statから取得したアプリケーションIDの読み込み
appId = YAML.load_file('./conf/config.yml')["appId"]
#統計表IDの読み込み
statsDataId = YAML.load_file('./conf/config.yml')["statsDataId"]
#銘柄(H27年基準)の読み込み
cdCat01 = YAML.load_file('./conf/config.yml')["cdCat01"]
#標準地域コードの読み込み
cdArea = YAML.load_file('./conf/config.yml')["cdArea"]
return analysisPrice(connectionAPI(url, appId, statsDataId, cdCat01, cdArea))
end
#API接続部
def connectionAPI(url, appId, statsDataId, cdCat01, cdArea)
#http接続クライアントの生成
client = HTTPClient.new
#クエリの設定
query = {
'appId' => appId,
'statsDataId' => statsDataId,
'cdCat01' => cdCat01,
'cdArea' => cdArea
}
#APIリクエスト
res = client.get(url, query)
#ハッシュ化して返却
return JSON.parse(res.body)
end
#connectionAPIで出力されたハッシュを解析
def analysisPrice(hash)
info = PriceInfo.new
#出力したい情報をdigメソッドで掘り出すinfo.statistics_name=(convertNil(hash.dig(@@GET_STATS_DATA, @@STATISTICAL_DATA, @@TABLE_INF, @@STATISTICS_NAME)))
info.title=(convertNil(hash.dig(@@GET_STATS_DATA, @@STATISTICAL_DATA, @@TABLE_INF, @@TITLE)))
info.cycle=(convertNil(hash.dig(@@GET_STATS_DATA, @@STATISTICAL_DATA, @@TABLE_INF, @@CYCLE)))
info.updated_date=(convertNil(hash.dig(@@GET_STATS_DATA, @@STATISTICAL_DATA, @@TABLE_INF, @@UPDATED_DATE)))
info.class_obj=(convertNil(hash.dig(@@GET_STATS_DATA, @@STATISTICAL_DATA, @@CLASS_INF, @@CLASS_OBJ)))
info.value=(convertNil(hash.dig(@@GET_STATS_DATA, @@STATISTICAL_DATA, @@DATA_INF, @@VALUE)))
return info
end
#nil判定、nilの場合は「-」を返却
def convertNil(value)
return value == nil ? "-" : value
end
end
【ソースコード】src/PriceInfo.rb
このファイルで欲しい情報を抜き出すための準備をしています。
class PriceInfo
attr_accessor :get_stats_data,
:statistical_data,
:table_inf,
:statistics_name,
:title,
:cycle,
:updated_date,
:data_inf,
:value,
:time
#コンストラクタ
def initialize()
end
#ルートパラメータ
def get_stats_data()
@get_stats_data
end
def get_stats_data=(value)
@get_stats_data = value
end
#統計データの情報
def statistical_data()
@statistical_data
end
def statistical_data=(value)
@statistical_data = value
end
#############################
#表データの情報
def table_inf()
@table_inf
end
def table_inf=(value)
@table_inf = value
end
#調査分類
def statistics_name()
@statistics_name
end
def statistics_name=(value)
@statistics_name = value
end
#表のタイトル
def title()
@title
end
def title=(value)
@title = value
end
#調査周期
def cycle()
@cycle
end
def cycle=(value)
@cycle = value
end
#更新日時
def updated_date()
@updated_date
end
def updated_date=(value)
@updated_date = value
end
#############################
#クラス情報
def class_inf()
@class_inf
end
def class_inf=(value)
@class_inf = value
end
#クラスオブジェクト情報
def class_obj()
@class_obj
end
def class_obj=(value)
@class_obj = value
end
#############################
#データの情報
def data_inf()
@data_inf
end
def data_inf=(value)
@data_inf = value
end
#詳細データの情報
def value()
@value
end
def value=(value)
@value = value
end
#調査時期の情報
def time()
@time
end
def time=(value)
@time = value
end
end
【ソースコード】test/test.rb
最後にターミナル上でプログラムを動かすファイルです。
欲しい情報を抜き出すために、JSONデータの階層を把握するのにかなり手間取りました。
前述のJSON Formatterが無ければ出来ていなかったと思います。
require "./src/Price"
require "./src/PriceInfo"
#出力のためにデータを作成する
priceobj = Price.new
info = priceobj.doProcess()
#出力部
puts "【帳票分類】#{info.statistics_name}"
puts "【帳票名】#{info.title}"
puts "【調査周期】:#{info.cycle}"
puts "【最終更新日】:#{info.updated_date}"
puts "【調査品目】:#{info.class_obj[1]['CLASS']['@name']}"
puts "【対象地域】:#{info.class_obj[2]['CLASS']['@name']}"
puts "【データ】↓↓"
info.value.each do |n|
puts " #{n['@time'][0..3]}年#{n['@time'][6..7]}月:#{n['$']}円"
end
puts "以上です。"
【結果】ターミナル上の出力
無事、欲しい情報を抜き出すことが出来ました!
カレーパンの価格安定しすぎ。笑
$ ruby test/test.rb
【帳票分類】小売物価統計調査(動向編)
【帳票名】主要品目の都市別小売価格-県庁所在市及び人口15万以上の市(2015年1月~)
【調査周期】:月次
【最終更新日】:2019-10-18
【調査品目】:1023 カレーパン
【対象地域】:特別区部
【データ】↓↓
2019年09月:101円
2019年08月:101円
2019年07月:102円
2019年06月:103円
2019年05月:103円
2019年04月:105円
2019年03月:104円
2019年02月:106円
2019年01月:106円
2018年12月:106円
2018年11月:106円
2018年10月:106円
2018年09月:105円
2018年08月:105円
2018年07月:106円
2018年06月:103円
2018年05月:105円
2018年04月:105円
2018年03月:105円
2018年02月:104円
2018年01月:106円
2017年12月:107円
2017年11月:106円
2017年10月:105円
2017年09月:105円
2017年08月:106円
2017年07月:106円
2017年06月:105円
2017年05月:102円
2017年04月:104円
2017年03月:102円
2017年02月:103円
2017年01月:103円
2016年12月:103円
2016年11月:103円
2016年10月:103円
2016年09月:103円
2016年08月:101円
2016年07月:102円
2016年06月:101円
2016年05月:103円
2016年04月:101円
2016年03月:103円
2016年02月:106円
2016年01月:104円
2015年12月:105円
2015年11月:105円
2015年10月:105円
2015年09月:103円
2015年08月:104円
2015年07月:102円
2015年06月:102円
2015年05月:103円
2015年04月:102円
2015年03月:100円
2015年02月:98円
2015年01月:100円
以上です。
2019/11/03 追記 グラフ風にしてみた
出力データの視認性を上げるようコードを改良してみました。
ついでに変動幅なども出ます。
ソースコード
require "./src/Price"
require "./src/PriceInfo"
#出力のためにデータを作成する
priceobj = Price.new
info = priceobj.doProcess()
#出力部
puts "【帳票分類】 #{info.statistics_name}"
puts "【帳票名】 #{info.title}"
puts "【調査周期】 #{info.cycle}"
puts "【最終更新日】 #{info.updated_date}"
puts "【調査品目】 #{info.class_obj[1]['CLASS']['@name']}"
puts "【対象地域】 #{info.class_obj[2]['CLASS']['@name']}"
puts ""
price_array = info.value.map{|h| h['$'].to_i}
puts info.value.class
price_max = price_array.max
price_min = price_array.min
price_average = (price_max + price_min)/2
price_median = price_array.sort[price_array.size / 2]
price_width = price_max.to_r - price_min.to_r
price_split = (price_width / 19)
puts "【最高価格】 #{price_max}円"
puts "【最低価格】 #{price_min}円"
puts "【平均価格】 #{price_average}円"
puts "【中央値】 #{price_median}円"
puts "【変動幅】 #{price_width.to_i}円"
puts "【時系列データ】"
info.value.each do |n|
year = n['@time'][0..3]
month = n['@time'][6..7]
price = n['$'].to_i
graph_value = ((price - price_min) / price_split).to_i
puts "#{year}年#{month}月 #{":" * (graph_value + 1)}#{" " * (20 - graph_value)}#{price}円"
end
puts "以上です。"
出力結果
今回は大阪市のPC価格の変動を確認。
❯ ruby test/test.rb
【帳票分類】 小売物価統計調査(動向編)
【帳票名】 主要品目の都市別小売価格-県庁所在市及び人口15万以上の市(2015年1月~)
【調査周期】 月次
【最終更新日】 2019-10-18
【調査品目】 9077 パーソナルコンピュータ
【対象地域】 大阪市
Array
【最高価格】 200765円
【最低価格】 119583円
【平均価格】 160174円
【中央値】 148377円
【変動幅】 81182円
【時系列データ】
2019年09月 ::::::::: 154563円
2019年08月 :::::::: 151014円
2019年07月 ::::::: 148052円
2019年06月 :::::::: 151909円
2019年05月 :::::::: 151010円
2019年04月 ::::::::: 157479円
2019年03月 :::::::::: 160118円
2019年02月 ::::::: 148377円
2019年01月 :::::::: 150382円
2018年12月 ::::::::: 154991円
2018年11月 ::::::::: 157166円
2018年10月 :::::::::: 161019円
2018年09月 ::::::::::: 162618円
2018年08月 : 122910円
2018年07月 : 119583円
2018年06月 : 122052円
2018年05月 :: 125589円
2018年04月 :::: 132722円
2018年03月 ::: 128612円
2018年02月 :::: 132769円
2018年01月 ::::: 139423円
2017年12月 :: 127840円
2017年11月 :: 125689円
2017年10月 ::: 129954円
2017年09月 ::::: 136801円
2017年08月 ::::: 139424円
2017年07月 ::: 130937円
2017年06月 ::: 132135円
2017年05月 ::::: 136986円
2017年04月 :::::: 143798円
2017年03月 :::::::::: 159537円
2017年02月 ::::::::::: 166294円
2017年01月 ::::: 139415円
2016年12月 ::::::: 147539円
2016年11月 ::: 130639円
2016年10月 : 123675円
2016年09月 :: 125277円
2016年08月 ::: 131502円
2016年07月 ::::: 139811円
2016年06月 :::::::: 151043円
2016年05月 ::::: 139552円
2016年04月 :::::: 142751円
2016年03月 ::::::: 148575円
2016年02月 :::::::::: 159789円
2016年01月 :::::: 144920円
2015年12月 ::::::: 148945円
2015年11月 :::::::::: 158397円
2015年10月 ::::::::::: 165929円
2015年09月 :::::::::::: 169976円
2015年08月 ::::::::::::: 172414円
2015年07月 :::::::::::::: 178817円
2015年06月 :::::::::::::::::::: 200765円
2015年05月 ::::::::::: 165267円
2015年04月 ::::::::::::: 171534円
2015年03月 ::::::::::::::: 181252円
2015年02月 ::::::::::::::::::: 196497円
2015年01月 :::::::::::: 167964円
以上です。
おわりに
初めてまともに外部からデータを引っ張ってみたので、かなり勉強になりました!
まだまだ冗長なコードだなと反省点は多いです。
もしこうした方がいい、などご指摘頂けるのであればコメント欄に記載して頂ければ幸いです。
これを応用すればWeb上でグラフを自動描画したり、幅が広がりそうでワクワクしますね!
この記事がどなたかのお役に立てればと思います。
参考にさせて頂いたサイト(いつもありがとうございます)
個人でも使える!おすすめAPI一覧 - Qiita
政府統計の総合窓口(e-Stat)−API機能へようこそ | 政府統計の総合窓口(e-Stat)−API機能
政府統計の総合窓口(e-Stat)のAPIを使ってみよう - Qiita
小売物価統計でガソリン価格を調べる - Qiita
Rubyを使って天気予報を取得してみた - Qiita
ディレクトリ構成図を書くときに便利な記号 - Qiita