HTML の表 (<table> タグ) をスクレイピングする時も pandas が超便利

HTML の表をスクレイピングするのは結構だるい作業です。

私は以前は、単純な HTML であれば、うまく特徴を見つけて awk や sed を作ったり、
Perl の正規表現で取り出したり、 Google Chrome のコンソールから XPath を使って取り出すような苦労をやっていました。

ところで pandas というとデータ解析用のツールとして主流ではあるのですが、
意外にも HTML からのデータ入力も可能になっていて、これが表のスクレイピングにはかなり楽だということがわかりました。
なので紹介してみます。

# サンプルに使うページ

以下で示すサンプルに国税庁の所得税の税率のページを使うことにしました。
https://www.nta.go.jp/taxes/shiraberu/taxanswer/shotoku/2260.htm
（2019.9.28 移転したようなので、URLを修正しました）

こんな感じの表があります。

```html
<table width="99%" border="1" cellpadding="5" cellspacing="0" class="datatable">
<caption>
所得税の速算表
</caption>
<tr>
<th width="62%" scope="col"> 課税される所得金額</th>
<th width="13%" scope="col"> 税率</th>
<th width="25%" scope="col"> 控除額</th>
</tr>
<tr>
<th align="left" scope="row"> 195万円以下</th>
<td align="center"> 5％</td>
<td align="right"> 0円</td>
</tr>
<tr>
<th align="left" scope="row"> 195万円を超え&emsp;330万円以下</th>
<td align="center"> 10％</td>
<td align="right"> 97,500円</td>
</tr>
<tr>
<th align="left" scope="row"> 330万円を超え&emsp;695万円以下</th>
<td align="center">20％</td>
<td align="right">427,500円</td>
</tr>
<tr>
<th align="left" scope="row"> 695万円を超え&emsp;900万円以下</th>
<td align="center">23％</td>
<td align="right">636,000円</td>
</tr>
<tr>
<th align="left" scope="row"> 900万円を超え&emsp;1,800万円以下</th>
<td align="center"> 33％</td>
<td align="right"> 1,536,000円</td>
</tr>
<tr>
<th align="left" scope="row"> 1,800万円を超え4,000万円以下</th>
<td align="center"> 40％</td>
<td align="right"> 2,796,000円</td>
</tr>
<tr>
<th align="left" scope="row"> 4,000万円超</th>
<td align="center"> 45％</td>
<td align="right"> 4,796,000円</td>
</tr>
</table>
```

また使用した Python のバージョンは 3.4.2 です。

# インストール

```shell-session
$ pip install pandas lxml html5lib BeautifulSoup4
```

pip を使ってインストールします。
pandas だけでなく read_html が使うパッケージも lxml, html5lib, BeautifulSoup4 をインストールする必要があります。

# 実際に使用

以下ではインタラクティブシェル上での挙動を説明します。

## インポート

```
$ python3
>>> import pandas
```

## 読み取り

```pycon
>>> url = 'https://www.nta.go.jp/taxanswer/shotoku/2260.htm'
>>> fetched_dataframes = pandas.io.html.read_html(url)
```

たった 2 行でスクレイピングは終わりです。

1. URL を指定
2. pandas.io.html.read_html で URL を読み取る

これだけ。

## それぞれの表にアクセス

取得した表はリストと同じようにインデクスでアクセスできます。

```pycon
>>> fetched_dataframes[0]
                    0    1           2
0           課税される所得金額   税率         控除額
1             195万円以下   5％          0円
2    195万円を超え　330万円以下  10％     97,500円
3    330万円を超え　695万円以下  20％    427,500円
4    695万円を超え　900万円以下  23％    636,000円
5  900万円を超え　1,800万円以下  33％  1,536,000円
6            1,800万円超  40％  2,796,000円
>>> fetched_dataframes[1]
                      0    1           2
0             課税される所得金額   税率         控除額
1               195万円以下   5％          0円
2      195万円を超え　330万円以下  10％     97,500円
3      330万円を超え　695万円以下  20％    427,500円
4      695万円を超え　900万円以下  23％    636,000円
5    900万円を超え　1,800万円以下  33％  1,536,000円
6  1,800万円を超え　4,000万円以下  40％  2,796,000円
7              4,000万円超  45％  4,796,000円
>>> fetched_dataframes[2]
                    0    1           2
0           課税される所得金額   税率         控除額
1             330万円以下  10％          0円
2    330万円を超え　900万円以下  20％    330,000円
3  900万円を超え　1,800万円以下  30％  1,230,000円
4            1,800万円超  37％  2,490,000円
>>>
```

# 保存

もちろんこのままでも pandas のデータフレームとして [色々と便利なアクセスが可能] (http://pandas.pydata.org/pandas-docs/dev/comparison_with_sql.html) なのですが、他の言語やフレームワークとの連携も考え CSV ファイルでの保存も説明します。

pandas のデータフレームの保存方法が使えるので、 to_csv メソッドにファイル名を指定すれば保存できます。

```pycon
>>> fetched_dataframes[0].to_csv('heisei19to26.csv')
>>> fetched_dataframes[1].to_csv('heisei27to.csv')
>>> fetched_dataframes[2].to_csv('heisei11to18.csv')
```

```csv:heisei27to.csv
,0,1,2
0,課税される所得金額,税率,控除額
1,195万円以下,5％,0円
2,195万円を超え　330万円以下,10％,"97,500円"
3,330万円を超え　695万円以下,20％,"427,500円"
4,695万円を超え　900万円以下,23％,"636,000円"
5,"900万円を超え　1,800万円以下",33％,"1,536,000円"
6,"1,800万円を超え　4,000万円以下",40％,"2,796,000円"
7,"4,000万円超",45％,"4,796,000円”
```

超便利。

# 免責

引用したデータは国税庁が持つデータです。筆者は解説のために引用したのみで、
筆者は記載している税率についての正誤・法的な責任は負いません。