Python, pandasでwebページの表(htmlのtable)をスクレイピング
pandasを使うと、webページの表(table
タグ)のスクレイピングが簡単にできる。DataFrame
として取得したあとで、もろもろの処理を行ったり、csvファイルとして保存したりすることももちろん可能。
なお、webページの表をコピーして、クリップボードの内容をDataFrame
として取得する方法もある。以下の記事参照。
ここでは、具体例を含めて以下の内容を説明する。
pandas.read_html()
でデータ取得- Yahoo!ファイナンスから時価総額ランキングを取得
- WikipediaのPythonのページからバージョンの履歴を取得
- DataFrameを後処理
なお、具体例については対象のWebサイトの更新によってサンプルコードが動かなくなっている可能性があるので注意。また、以下のサンプルコードはpandas0.20.3
のもの。バージョンによって挙動が異なる場合がある。
pandas.read_html()でデータ取得
pandas.read_html()
を使う。
lxml
、html5lib
、beautifulsoup4
が必要なので、pip
でインストールしておく。
$ pip install lxml html5lib beautifulsoup4
Yahoo!ファイナンスから時価総額ランキングを取得
Yahoo!ファイナンスの時価総額ランキングの表を取得してみる。
import pandas as pd
url = 'https://info.finance.yahoo.co.jp/ranking/?kd=4'
dfs = pd.read_html(url)
引数にurl
のみを指定すると、ページ内の表をすべて取得しDataFrame
のリストとして返す。例のように表が1つしかない場合も、長さ1のリストとして返される。
print(len(dfs))
# 1
中身は以下のようになっている。
print(dfs[0].head())
# 順位 コード 市場 名称 取引値 発行済み株式数 時価総額(百万円) 単元株数 \
# 0 1 7203 東証1部 トヨタ自動車(株) 09/15 6480 3262997492 21144224
# 1 2 9432 東証1部 日本電信電話(株) 09/15 5127 2096394470 10748214
# 2 3 9437 東証1部 (株)NTTドコモ 09/15 2539.5 3899563000 9902940
# 3 4 8306 東証1部 (株)三菱UFJフィナンシャル・グループ 09/15 681.6 14027694920 9561277
# 4 5 9984 東証1部 ソフトバンクグループ(株) 09/15 8587 1100660365 9451371
# 掲示板 Unnamed: 9
# 0 100 掲示板
# 1 100 掲示板
# 2 100 掲示板
# 3 100 掲示板
# 4 100 掲示板
名称
と時価総額
のみを表示。
print(dfs[0][['名称', '時価総額(百万円)']].head())
# 名称 時価総額(百万円)
# 0 トヨタ自動車(株) 3262997492
# 1 日本電信電話(株) 2096394470
# 2 (株)NTTドコモ 3899563000
# 3 (株)三菱UFJフィナンシャル・グループ 14027694920
# 4 ソフトバンクグループ(株) 1100660365
よく見てみると時価総額
の値がおかしい。取引値
のあとからコラム名(列名)がずれている。
これは、元のページを見ると分かるように、取引値
に日付と価格の2つの列が含まれているのが原因。
columns
を更新すればよい。
dfs[0].columns = ['順位', 'コード', '市場', '名称', '時刻', '取引値', '発行済み株式数', '時価総額(百万円)', '単元株数', '掲示板']
print(dfs[0][['名称', '時価総額(百万円)']].head())
# 名称 時価総額(百万円)
# 0 トヨタ自動車(株) 21144224
# 1 日本電信電話(株) 10748214
# 2 (株)NTTドコモ 9902940
# 3 (株)三菱UFJフィナンシャル・グループ 9561277
# 4 ソフトバンクグループ(株) 9451371
このようにセルが結合されていたりするとうまく取得できない場合があるので注意。
WikipediaのPythonのページからバージョンの履歴を取得
同様に、WikipediaのPythonのページの表を取得する。
url = 'https://ja.wikipedia.org/wiki/Python'
dfs = pd.read_html(url)
ターゲットは、バージョンとリリース日の表。バージョン2系と3系の2つの表があるが、取得したDataFrame
のリストの長さ(含まれるDataFrame
の数)は2よりも大きい。
print(len(dfs))
# 15
これは、Wikipediaではいろいろな部分がtable
タグで表現されているのが原因。
こんなときに便利なのが引数match
。文字列を指定すると、その文字列が含まれる表のみを取得できる。
dfs = pd.read_html(url, match='リリース日')
print(len(dfs))
# 2
2つの表がリストとして抽出される。
print(dfs[0])
# 0 1
# 0 バージョン リリース日[17]
# 1 2.0 2000年10月16日
# 2 2.1 2001年4月15日
# 3 2.2 2001年12月21日
# 4 2.3 2003年7月29日
# 5 2.4 2004年11月30日
# 6 2.5 2006年9月19日
# 7 2.6 2008年10月1日
# 8 2.7 2010年7月4日
print(dfs[1])
# 0 1
# 0 バージョン リリース日[17]
# 1 3.0 2008年12月3日
# 2 3.1 2009年6月27日
# 3 3.2 2011年2月20日
# 4 3.3 2012年9月29日
# 5 3.4 2014年3月16日
# 6 3.5 2015年9月13日
# 7 3.6 2016年12月23日
バージョン
とリリース日
をヘッダー(DataFrame
のcolumns
属性)にしたい場合は、引数header
を使う。ここで指定した行がヘッダーになる。
dfs = pd.read_html(url, match='リリース日', header=0)
print(len(dfs))
# 2
print(dfs[0])
# バージョン リリース日[17]
# 0 2.0 2000年10月16日
# 1 2.1 2001年4月15日
# 2 2.2 2001年12月21日
# 3 2.3 2003年7月29日
# 4 2.4 2004年11月30日
# 5 2.5 2006年9月19日
# 6 2.6 2008年10月1日
# 7 2.7 2010年7月4日
print(dfs[1])
# バージョン リリース日[17]
# 0 3.0 2008年12月3日
# 1 3.1 2009年6月27日
# 2 3.2 2011年2月20日
# 3 3.3 2012年9月29日
# 4 3.4 2014年3月16日
# 5 3.5 2015年9月13日
# 6 3.6 2016年12月23日
先のYahoo!ファイナンスの時価総額の例のように<thead>
タグが使われている場合はその行がヘッダーになる。引数header
を指定する必要はない。
また、上のコードはpandas0.20.3
のものだが、0.21.0
以降はヘッダーを推定するようになった。WikipediaのPythonのページの例でも引数header
を指定しなくても先頭行がヘッダーとして取得される。
DataFrameを後処理
一度DataFrame
で取得してしまえば、後処理も簡単。
例として、上で取得したPythonのバージョンとリリース日の2つの表に対して、concat()
による連結とsort_values()
による並べ替えを行う。
- 関連記事: pandas.DataFrame, Seriesを連結するconcat
- 関連記事: pandas.DataFrame, Seriesをソートするsort_values, sort_index
df = pd.concat([dfs[0], dfs[1]], ignore_index=True).sort_values('リリース日[17]')
print(df)
# バージョン リリース日[17]
# 0 2.0 2000年10月16日
# 2 2.2 2001年12月21日
# 1 2.1 2001年4月15日
# 3 2.3 2003年7月29日
# 4 2.4 2004年11月30日
# 5 2.5 2006年9月19日
# 6 2.6 2008年10月1日
# 8 3.0 2008年12月3日
# 9 3.1 2009年6月27日
# 7 2.7 2010年7月4日
# 10 3.2 2011年2月20日
# 11 3.3 2012年9月29日
# 12 3.4 2014年3月16日
# 13 3.5 2015年9月13日
# 14 3.6 2016年12月23日
さらに、to_csv()
でCSVファイルとして保存する。
df.to_csv('data/dst/pandas_read_html_sample.csv')
出来上がりのCSVファイルは以下のようになる。
,バージョン,リリース日[17]
0,2.0,2000年10月16日
2,2.2,2001年12月21日
1,2.1,2001年4月15日
3,2.3,2003年7月29日
4,2.4,2004年11月30日
5,2.5,2006年9月19日
6,2.6,2008年10月1日
8,3.0,2008年12月3日
9,3.1,2009年6月27日
7,2.7,2010年7月4日
10,3.2,2011年2月20日
11,3.3,2012年9月29日
12,3.4,2014年3月16日
13,3.5,2015年9月13日
14,3.6,2016年12月23日
いろいろ設定しないといけないこともあるが、かなりシンプルにwebページの表をCSVファイルとして保存できる。便利。