HTMLの表のスクレイピング・MultiIndex

ここでは例として大気中の放射線量/1時間単位 (新宿)2011/03/14 ~ 2011/03/20 |環境放射線測定結果のような表をpandasで読み込む方法を説明します。

注意すべき点は、表の見出しにセル結合が使ってあって、列見出しに階層構造があることです:

測定日時
(Measurement time)
線量率 (dose rate)
μGy/h (microgray per hour)
最大値
(max)
最小値
(min)
平均値
(average)
2011/03/20 23:00~23:59 0.0515 0.0465 0.0494
2011/03/20 22:00~22:59 0.0524 0.0405 0.0478
2011/03/20 21:00~21:59 0.0464 0.0416 0.0443

表自体をスクレイピングするのは簡単です。あらかじめpandasとlxmlがインストールされているとします(pip install pandaspip install lxml):

import pandas as pd

tables = pd.read_html('https://monitoring.tmiph.metro.tokyo.lg.jp/report/shinjuku/mon_air_week_201112w.html')
len(tables)
1

最初の表を取り出します:

df = tables[0]

この df を表示すると、次のようになります(見やすいように余分なスペースを削除しました):

df
     測定日時(Measurement time) 線量率 (dose rate)μGy/h (microgray per hour)
     測定日時(Measurement time) 最大値(max) 最小値(min) 平均値(average)
0    2011/03/20 23:00~23:59     0.0515   0.0465       0.0494
1    2011/03/20 22:00~22:59     0.0524   0.0405       0.0478
2    2011/03/20 21:00~21:59     0.0464   0.0416       0.0443
3    2011/03/20 20:00~20:59     0.0470   0.0414       0.0443
4    2011/03/20 19:00~19:59     0.0468   0.0425       0.0445
...                      ...        ...      ...          ...
[168 rows x 4 columns]

列見出しに階層構造があることがわかります。元の列名の途中にある改行(<br>)は消え、1列目の列名が二つに複製されました。列名を表示してみます:

df.columns
MultiIndex([(                   '測定日時(Measurement time)', ...),
            ('線量率 (dose rate)μGy/h (microgray per hour)', ...),
            ('線量率 (dose rate)μGy/h (microgray per hour)', ...),
            ('線量率 (dose rate)μGy/h (microgray per hour)', ...)],
           )
df.columns[0]
('測定日時(Measurement time)', '測定日時(Measurement time)')
df.columns[1]
('線量率 (dose rate)μGy/h (microgray per hour)', '最大値(max)')
df.columns[2]
('線量率 (dose rate)μGy/h (microgray per hour)', '最小値(min)')
df.columns[3]
('線量率 (dose rate)μGy/h (microgray per hour)', '平均値(average)')

このように、列名にあたる部分がタプル(tuple)になっています。一番右の列を取得するには

df[('線量率 (dose rate)μGy/h (microgray per hour)', '平均値(average)')]

とするか、または番号で df.iloc[:,3] とします。手っ取り早くプロットしてみます:

import matplotlib.pyplot as plt

df.iloc[:,0] = [pd.Timestamp(x[:16]) for x in df.iloc[:,0]]
df.plot(0, 3)
plt.legend(['線量率(μGy/h)'])
plt.xlabel('')
plt.ylabel('')
新宿の線量率