pandasで入れ子になったtableがあるhtmlを読込む
業務でここのところずっとPythonを使っています。主にAIだの機械学習だのDNNだのとその辺の比較的今風の分野を触ってるんですが、それも飽きてきたので何か業務以外の事をやりたい。とは言いつつ何をやるでもないのでランサーズの案件を眺めてみる事に。で、プロジェクト案件の課題をみつけて勝手に実装してみるという暇つぶしを考案。アウトプットしていかないと多分この暇つぶしも続かないと思うのでここにメモって行こうというそんな企画です。
pandasでhtmlを読込む
web系やってる人は今更感強いのでしょうか。業務ではweb系の開発をほぼしないのでこんな事できるんだーって感じでした。
1 2 3 4 5 |
import pandas as pd url = 'http://xxxx.com' df = pd.read_html(url) |
urlで指定したページ内にあるtableタグをDataFrameとして抜き出してくれる便利な関数ですが、慣れないと動作がちょっと難しい。今回対象にした案件は三菱UFJ銀行の外国為替相場一覧表。このページから中国元の値を抜き出そうという内容です。で、さっきのurlにリンクのurlをぶっこんで実行してみるとDataFrameのリストが返却されます。要素数は15。もうどうなっちゃってんのって感じですね。実はこのページ、僕が見てる今現在、文章も何も全部テーブルで構成されています。それもテーブルの要素にテーブルがいくつも入れ子になっているという状況。うーむ、目的の表だけ欲しいのに。
read_htmlはどうhtmlを読み込むのか
三菱UFJ銀行のページをいきなり使うとこんがらがってしまうので一個づつ実験しながら見ていきましょう。まずは単純にtableが一個の場合。実験に使うPythonコードは以下のとおり。
1 2 3 4 5 6 7 8 9 |
import pandas as pd path = './sample.html' with open(path, 'r') as f html_data = f.read() df_list = pd.read_html(html_data) for df in df_list: print(df) |
実験データとなるhtmlは以下のとおり。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<table> <tr> <th>aaa</th> <th>bbb</th> </tr> <tr> <td>1</td> <td>2</td> </tr> <tr> <td>11</td> <td>22</td> </tr> </table> |
2x3のテーブルです。これをPythonのコードと同じディレクトリに保存して読み込ませればOKです。ちなみにread_html()は対象のurlを引数に持たせれば指定したページのhtmlを読み取りますが、例のようにローカルに置いてあるhtmlファイルを読ませて取り出したテキストデータを引数に渡しても同じことが出来ます。結果は以下の通り。
1 2 3 4 |
0 1 0 aaa bbb 1 1 2 2 11 11 |
ちゃんとhtmlに書いたテーブルがDataFrameとして読み込まれていますね。では次にテーブルが2つだった場合はどうなるのでしょうか。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<table> <tr> <th>aaa</th> <th>bbb</th> </tr> <tr> <td>1</td> <td>2</td> </tr> <tr> <td>11</td> <td>22</td> </tr> </table> <table> <tr> <th>ddd</th> <th>eee</th> </tr> <tr> <td>111</td> <th>222</th> </tr> </table> |
実行結果はこちらです。
1 2 3 4 5 6 7 8 9 10 11 |
# 1つめ 0 1 0 aaa bbb 1 1 2 2 11 22 # 2つめ 0 1 0 ddd eee 1 111 222 |
区切りがくっついてしまってちょいわかりずらいですがDataFrameは2つ読み込まれています。ここまでは実験するまでもなくイメージ出来ていたのではないでしょうか。では今度はこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<table> <tr> <th>aaa</th> <th>bbb</th> </tr> <tr> <td>1</td> <td>2</td> </tr> <tr> <td>11</td> <table> <tr> <th>ddd</th> <th>eee</th> </tr> <tr> <td>111</td> <th>222</th> </tr> </table> </tr> </table> |
今度はテーブルの中にテーブルが定義されている入れ子形式のテーブルになります。さて
、これをpandasで読み込んだ場合はどうなるのでしょうか。気になる結果はこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 1つめ 0 1 2 3 4 0 aaa bbb NaN NaN NaN 1 1 2 NaN NaN NaN 2 11 ddd eee 111.0 222.0 3 ddd eee NaN NaN NaN 4 111 222 NaN NaN NaN # 2つめ 0 1 0 ddd eee 1 111 222 |
こちらもテーブルは2つ読み込まれています。が、1つめの読み込み結果がどうも妙な事になっています。2x3で定義したはずのテーブルが5x5に拡張されてしまっています。入れ子テーブルを定義した(1,2)に2つめのテーブルの(0,0)がきており、そこを中心におかしな並びでデータが展開されているようです。下方向に拡張された部分には2つめのテーブルが丸っと、右方向に拡張された部分には2つめのテーブルが横並びにされています。しかも111.0, 222.0と表示されており型も何やら変換されていそうです。この例では(1,3)に入れ子テーブルを配置しており、テーブルの端っこだった為、この様な拡張のされ方をしていますが、(0,1)にテーブルを2つめのテーブルを差し込んだらどうなるのでしょうか。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<table> <tr> <th>aaa</th> <th>bbb</th> </tr> <tr> <table> <tr> <th>ddd</th> <th>eee</th> </tr> <tr> <td>111</td> <th>222</th> </tr> </table> <td>2</td> </tr> <tr> <td>11</td> <td>22</td> </tr> </table> |
実験結果はこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 1つめ 0 1 2 3 4 0 aaa bbb NaN NaN NaN 1 ddd eee 111.0 222.0 2.0 2 ddd eee NaN NaN NaN 3 111 222 NaN NaN NaN 4 11 22 NaN NaN NaN # 2つめ 0 1 0 ddd eee 1 111 222 |
2つめのテーブルを差し込んだ箇所から右下にもとのテーブルを押し広げる様にテーブルが拡張されていることがわかります。(4,1)にある2.0は表示方法が変わっていることからわかる通り型が変更されています。調べてみるとstr→floatになっている様です。pandasは列方向に型を変更しているのかもしれません。
外国為替相場一覧を読んでみる
pandasの入れ子に形状についての法則はわかってきました。では今回の目的である三菱UFJ銀行の外国為替相場一覧を読み込んでみたいと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import pandas as pd target_url = 'https://www.bk.mufg.jp/gdocs/kinri/list_j/kinri/kawase.html' def read_rate_table(url): # urlからDataFrameを取り出す df_list = pd.read_html(url) # 9番目のDataFrameが今回のターゲット rate_df = df_list[9] # 不要な部分を取り除きつつヘッダー行を確保 header_list = rate_df.values[3][:-1] # テーブルのいらない部分を削除 rate_df = rate_df.drop([0, 1, 2, 3]) # 一番左の番号をインデックスに指定 rate_df = rate_df.set_index(0) # 取って置いたヘッダー行をくっつける rate_df.columns = header_list print(rate_df) if __name__ == '__main__': read_rate_table(data) |
この記事を書いている現在ではこのコードで目的の相場一覧を取得することが出来ました!一見してぐっちゃぐちゃに見えたテーブルも簡単な例からみてみると案外簡単な作りをしていることがわかりますね。しかし、pandas優秀。スクレイピング素人なのでとても便利に感じました。
これで為替相場系のスクレイピングであれば自由自在ではないでしょうか!
まとめ
今回はpandasでhtmlからテーブルデータを取ってくるをテーマに記事を書いてみました。web京の開発も楽しそうだ!というわけで今回は以上です!
ディスカッション
コメント一覧
まだ、コメントがありません