note.nkmk.me

Python, pandasでwebページの表(htmlのtable)をスクレイピング

Date: 2017-09-17 / tags: Python, pandas, スクレイピング

pandasを使うと、webページの表(tableタグ)のスクレイピングが簡単にできる。DataFrameとして取得したあとで、もろもろの処理を行ったり、csvファイルとして保存したりすることももちろん可能。

なお、webページの表(tableタグ)をコピーして、クリップボードの内容をDataFrameとして取得する方法もある。以下の記事参照。

スポンサーリンク

pandas.read_html()でデータ取得

pandas.read_html()を使う。

lxmlhtml5libbeautifulsoup4が必要なので、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つの列が含まれているのが原因。

コラム名を更新すると問題なし。

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のリストの長さは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日

バージョンリリース日をヘッダーにしたい場合は、引数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日

DataFrameを後処理

一度DataFrameで取得してしまえば、後処理もいろいろできる。

例えば、上で取得したPythonのバージョンとリリース日の2つの表に対して、

  • concat()で連結
  • sort_values()で並べ替え

を行う。

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として取得することができた。便利。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事