Elixirのxmlパーサ Floki を利用して、政府統計 e-Stat を触ってみる

## 政府統計 e-Stat

各省庁が提供しているデータがAPI経由で取得できるようです。

[政府統計 e-Stat](http://www.e-stat.go.jp/api/)というサイトのようです。
トレーニングも兼ねて種々のデータをElixirでロードしたい、と思いました。

提供されるデータ形式は、xmlとjsonの２通りあるそうです。([^1])

Elixir、jsonのライブラリ等については、少しずつ触ってきて慣れてきたので、今後webスクレイピングするかも、ということでxmlのパーサライブラリに触ってみることにしました。

* https://github.com/philss/floki

---

今回のサンプルコードは下記においています。

* https://github.com/letusfly85/crawl_estat

---

## 利用手順

### 1. mix.exsにdepsを追加して、mix deps.get

```mix.exs
  def application do
    [applications: [:logger, :httpoison]]
  end

  defp deps do
    [
      {:mix_test_watch, "~> 0.2", only: [:dev, :test]},
      {:poison,    "~> 1.2"},
      {:httpoison, "~> 0.7"},
      {:floki, "~> 0.8"},
      {:credo, "~> 0.3", only: [:dev, :test]}
    ]
  end
```

httpアクセスするので、[HTTPoison](https://github.com/edgurgel/httpoison)も追加しています。

### 2. 統計表の一覧から、政府統計コードを指定して利用したい統計表IDを取得する

APIのversion 2.0のURLを`config.exs`で定義して、url文字列を生成しました。

```config/config.exs
use Mix.Config

config :crawl_estat, stats_list_url: "http://api.e-stat.go.jp/rest/2.0/app/getStatsList"
config :crawl_estat, stats_data_url: "http://api.e-stat.go.jp/rest/2.0/app/getStatsData"
```

```crawl_estat.ex
url = Application.get_env(:crawl_estat, :stats_list_url) <>
      "?appId=#{@api_id}&statsCode=#{stats_code}&surverYears=#{surver_years}"
```

指定しているパラメータ（[^2]）は、以下となります。

* api_id（必須、利用者はe-Statに登録して習得する）
* statsCode（必須、[サイト](http://www.e-stat.go.jp/api/api-data/)から選択して指定する）
* surverYears（任意、指定しないとたくさんデータが取れてしまう。。）

### 3. HTTPoisonでリクエストを発行し、XMLをパースする

以下のように、ノードにアクセスできるようです。
シンプルでわかりやすいですね＾＾

```ex
Floki.find(xml, "アトリビュート")
```

上記で指定したURLリクエストのレスポンスについては以下のように処理を施してみました。


```crawl_estat.ex
    prob_response = HTTPoison.get(url)
    case prob_response do
      {:ok, response} ->
        Logger.info(inspect Floki.find(response.body, "table_inf"))

        prob_ids = Floki.find(response.body, "table_inf")
        Enum.reduce(prob_ids, [], fn(prob_id, acc) ->
          case prob_id do
            {_, [{"id", id}], _} ->
              IO.inspect(id)
              [id|acc]
          end
        end)
    end
```

`response.body`が、以下のようなxmlの形式になっています。

```xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GET_STATS_DATA xsi:noNamespaceSchemaLocation="http://api.e-stat.go.jp/rest/2.0/schema/GetStatsData.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <RESULT>
        <STATUS>0</STATUS>
        <ERROR_MSG>正常に終了しました。</ERROR_MSG>
        <DATE>2016-04-04T11:23:30.692+09:00</DATE>
    </RESULT>
    <PARAMETER>
        <LANG>J</LANG>
        <STATS_DATA_ID>0003109558</STATS_DATA_ID>
        <DATA_FORMAT>X</DATA_FORMAT>

        <START_POSITION>1</START_POSITION>
        <METAGET_FLG>Y</METAGET_FLG>
    </PARAMETER>
    <STATISTICAL_DATA>
        <RESULT_INF>
            <TOTAL_NUMBER>10944</TOTAL_NUMBER>
            <FROM_NUMBER>1</FROM_NUMBER>
            <TO_NUMBER>10944</TO_NUMBER>
        </RESULT_INF>
        <TABLE_INF id="0003109558">
            <STAT_NAME code="00450012">生命表</STAT_NAME>
            <GOV_ORG code="00450">厚生労働省</GOV_ORG>
            <STATISTICS_NAME>生命表 簡易生命表</STATISTICS_NAME>
            <TITLE>簡易生命表</TITLE>
            <CYCLE>年次</CYCLE>
            <SURVEY_DATE>0</SURVEY_DATE>
            <OPEN_DATE>2015-09-30</OPEN_DATE>
            <SMALL_AREA>0</SMALL_AREA>
            <MAIN_CATEGORY code="02">人口・世帯</MAIN_CATEGORY>
            <SUB_CATEGORY code="03">人口動態</SUB_CATEGORY>
            <OVERALL_TOTAL_NUMBER>10944</OVERALL_TOTAL_NUMBER>
            <UPDATED_DATE>2015-12-02</UPDATED_DATE>
        </TABLE_INF>
```

のようになります。
ここから、いったん、`Floki.find(response.body, "table_inf")`をして、取り出しました。

以下のようなデータが取れます。

```
[{"table_inf", [{"id", "0003109570"}], [{"stat_name", [{"code", "00450012"}], ["生命表"]}, {"gov_org", [{"code", "00450"}], ["厚生労働省"]}, {"statistics_name", [], ["生命表 完全生命表"]},
{"title", [], ["完全生命表"]}, {"cycle", [], ["-"]}, {"survey_date", [], ["0"]}, {"open_date", [], ["2015-09-30"]}, {"small_area", [], ["0"]}, {"main_category", [{"code", "02"}], ["人口・世帯"]}, {"sub_category
", [{"code", "03"}], ["人口動態"]}, {"overall_total_number", [], ["3880"]}, {"updated_date", [], ["2015-12-02"]}]}, {"table_inf", [{"id", "0003109558"}], [{"stat_name", [{"code", "00450012"}], ["生命表"]}, {"go
v_org", [{"code", "00450"}], ["厚生労働省"]}, {"statistics_name", [], ["生命表 簡易生命表"]}, {"title", [], ["簡易生命表"]}, {"cycle", [], ["年次"]}, {"survey_date", [], ["0"]}, {"open_date", [], ["2015-09-30"]

}, {"small_area", [], ["0"]}, {"main_category", [{"code", "02"}], ["人口・世帯"]}, {"sub_category", [{"code", "03"}], ["人口動態"]}, {"overall_total_number", [], ["10944"]}, {"updated_date", [], ["2015-12-02"]}
]}]
```

tuple形式のデータがListで取れるようです。

そのあとは、`<TABLE_INF id="0003109558">`の`id`を取得したいので、`Enum.reduce`と`case`を利用しました。
ここらへんはもうちょっとシンプルにアクセスしたいのですが、ちょっとFlokiだと難しそうでした。

ソースコードも読んでみようと思います。

## 感想

* もうちょっとxml, htmlパース系のライブラリは探してみようと思いました。
* パターンマッチングが利用できるので、この手のスクレイピング系はElixirはRubyとは違う面白さがあるのかな、と思いました＾＾
* Parserコンビネータ、Elixirであったら触ってみたいです。
* 政府の統計情報、データベース直接触らせてもらえないのかしら。


## 参考

[^1]: http://www.e-stat.go.jp/api/api-spec/
[^2]: http://www.e-stat.go.jp/api/e-stat-manual/

---

本日は以上となります。