BeautifulSoup4でtableをパースする方法【Python, スクレイピング】
目次
BeautifulSoup4でtable要素をパースする
Pythonの外部ライブラリにHTML/XMLパーサーである「BeautifulSoup4(ビューティフル・スープ・フォー)」があります。
このライブラリはPythonによるスクレイピングなどでよく利用されるパーサーです。
このBeautifulSoup4を使うと、テーブル(table)要素も簡単にパースすることが出来ます。
この記事ではBeautifulSoup4でテーブル要素をパースする方法を解説します。
具体的には↓を見ていきます。
パース対象のテーブル要素
BeautifulSoup4の準備
1つ目のテーブルのパース
2つ目のテーブルのパース
パース対象のテーブル要素
今回パースするのはHTMLのtable
要素です。
table
は↓のタグで構成されます。
table
thead
tbody
tfoot
tr
th
td
テーブルの構成によって使われるタグは異なることが多いです。
今回は基本となる構成のテーブルをパースします。
1つ目は↓のテーブルです。
商品名 | 在庫量 | 値段 | 販売先 |
---|---|---|---|
おいしいリンゴ | 100 | 200円 | スーパー池本 |
赤いブドウ | 200 | 300円 | スーパー橋本 |
小さいメロン | 50 | 500円 | 田中商店 |
↑のテーブルのソースコード↓。
<table border="1"> <thead> <tr> <th>商品名</th> <th>在庫量</th> <th>値段</th> <th>販売先</th> </tr> </thead> <tbody> <tr> <td>おいしいリンゴ</td> <td>100</td> <td>200円</td> <td>スーパー池本</td> </tr> <tr> <td>赤いブドウ</td> <td>200</td> <td>300円</td> <td>スーパー橋本</td> </tr> <tr> <td>小さいメロン</td> <td>50</td> <td>500円</td> <td>田中商店</td> </tr> </tbody> </table>
thead
とtbody
が組み込まれているのが特徴です。
このタグは省略することも可能です。
2つ目は↓の構成のテーブルです。
商品名 | 在庫量 | 値段 | 販売先 |
---|---|---|---|
おいしいリンゴ | 100 | 200円 | スーパー池本 |
赤いブドウ | 200 | 300円 | スーパー橋本 |
小さいメロン | 50 | 500円 | 田中商店 |
↑のテーブルのソースコード↓。
<table border="1"> <tr> <th>商品名</th> <th>在庫量</th> <th>値段</th> <th>販売先</th> </tr> <tr> <td>おいしいリンゴ</td> <td>100</td> <td>200円</td> <td>スーパー池本</td> </tr> <tr> <td>赤いブドウ</td> <td>200</td> <td>300円</td> <td>スーパー橋本</td> </tr> <tr> <td>小さいメロン</td> <td>50</td> <td>500円</td> <td>田中商店</td> </tr> </table>
↑の構成はthead
やtbody
が省略されているのが特徴です。
この記事では、これら2つの構成のテーブルのパースを行います。
BeautifulSoup4の準備
BeautifulSoup4を使うにはbs4
パッケージからBeautifulSoup
クラスをインポートします。
from bs4 import BeautifulSoup
bs4.BeautifulSoup
クラスはbs4.element.Tag
を継承したクラスです。
よってbs4.BeautifulSoup
のオブジェクトで利用可能なメソッドのほとんどはbs4.element.Tag
のものです。
今回のテーブルのパースでは、このbs4.element.Tag
のメソッドを頻繁に使うことになります。
追って解説していきます。
1つ目のテーブルのパース
1つ目のテーブルのBeautifulSoupによるパースを行います。
BeautifulSoupでテーブルをパースしたら、それらのテーブルの情報はPythonのリストに保存していきます。
こうすることで、最終的にCSVのフォーマットなどに変形することも可能になります。
まず前提としてhtml
という変数に先ほどの1つ目のテーブルのコードを文字列で保存しておきます。
html = ''' <table> <thead> <tr> <th>商品名</th> <th>在庫量</th> <th>値段</th> <th>販売先</th> </tr> ~ 省略 ~ '''
このhtml
変数を使って実際のパースを行います。
from bs4 import BeautifulSoup html = ''' <table> <thead> ~ 省略 ~ ''' mat = [] # 保存先の行列 soup = BeautifulSoup(html, 'html.parser') # tableの取得 table = soup.find('table') # theadの解析 r = [] # 保存先の行 thead = table.find('thead') # theadタグを探す ths = thead.tr.find_all('th') for th in ths: # thead -> trからthタグを探す r.append(th.text) # thタグのテキストを保存 mat.append(r) # 行をテーブルに保存 # tbodyの解析 tbody = table.find('tbody') # tbodyタグを探す trs = tbody.find_all('tr') # tbodyからtrタグを探す for tr in trs: r = [] # 保存先の行 for td in tr.find_all('td'): # trタグからtdタグを探す r.append(td.text) # tdタグのテキストを保存 mat.append(r) # 出力 for r in mat: print(','.join(r)) # カンマ(,)で列を結合して表示
↑のように主要な解析はthead
の解析とtbody
の解析になります。
まず最初にtable
タグをsoup.find('table')
で取得します。
thead
タグはtr
タグを持っていて、そのtr
タグは複数のth
タグを持っています。
そのためtable.find('thead')
でthead
タグを取得し、thead.tr.find_all('th')
とやってth
タグをまとめて取得します。
bs4.element.Tag
のfind()
メソッドは第1引数のタグ名からタグを検索するメソッドです。
これの返り値もbs4.element.Tag
になっています。
find_all()
との違いは、find_all()
が条件に一致するタグをすべて取得するのに対し、find()
は条件に一致した最初のタグのみ取得する点です。
find_all()
の返り値はbs4.element.ResultSet
です。これはPythonのlist
を継承したクラスで、リストのように扱うことが出来ます。
find_all()
で取得した要素をfor
文で回し、th
タグを走査していきます。そしてth
タグの属性text
を参照し、それを行に保存します。
こうすることでthead
の列を保存しています。
tbody
タグについても同様です。
まずtable.find('tbody')
でtbody
タグを取得します。
tbody
内のtr
タグをまとめて取得し、それをfor
文で回します。
tr
タグ内のtd
タグを探し、それをfor
文で回しながらtd
タグのテキストを行に保存します。
行を保存し終わったらテーブルに行を追加します。
最後に出力のためmat
をfor
文で回します。
行列の行の列を','.join(r)
で結合し、カンマ区切りのフォーマット、つまりCSVに変換します。
↑のコードの出力は↓のようになります。
商品名,在庫量,値段,販売先 おいしいリンゴ,100,200円,スーパー池本 赤いブドウ,200,300円,スーパー橋本 小さいメロン,50,500円,田中商店
2つ目のテーブルのパース
2つ目のテーブルのコードをhtml
変数に保存しておきます。
html = ''' <table> <tr> <th>商品名</th> ~ 省略 ~ '''
2つ目のテーブルにはthead
やtbody
がありません。
あるのはtable
, tr
, th
, td
のみです。
そのため最初にtable
タグを取得したらtr
タグをまとめて取得します。
そしてマジックナンバーでtr
のリストを参照し、商品名などを取得します。
残りのtr
はfor
文で回し、行ごとに解析していきます。
from bs4 import BeautifulSoup html = ''' <table> <tr> <th>商品名</th> ~ 省略 ~ ''' mat = [] # 保存先の行列 soup = BeautifulSoup(html, 'html.parser') # tableの取得 table = soup.find('table') # trの解析 trs = table.find_all('tr') # ヘッダーの解析 r = [] # 保存先の行 tr = trs[0] for td in tr.find_all('th'): # thタグを走査する r.append(td.text) mat.append(r) # ボディの解析 for tr in trs[1:]: # 最初の行を飛ばしてfor文で回す r = [] # 保存先の行 for td in tr.find_all('td'): # tdタグを走査する r.append(td.text) mat.append(r) # 出力 for r in mat: print(','.join(r)) # カンマ(,)で列を結合して表示
thead
やtbody
タグがある構成のテーブルと比べると、解析は多少推測みたいなものが入っていて、エラーの考慮などが必要になってきます。
↑のコードの出力結果↓。
商品名,在庫量,値段,販売先 おいしいリンゴ,100,200円,スーパー池本 赤いブドウ,200,300円,スーパー橋本 小さいメロン,50,500円,田中商店
おわりに
BeautifulSoup4を使うと簡単にテーブルをパースすることが可能です。
スクレイピングに使ってみられてはいかがでしょうか。
(^ _ ^) | ユーニックス家の食卓 |