BeautifulSoup4でtableをパースする方法【Python, スクレイピング】
- 作成日: 2020-11-26
- 更新日: 2023-12-24
- カテゴリ: BeautifulSoup4
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を使うと簡単にテーブルをパースすることが可能です。
スクレイピングに使ってみられてはいかがでしょうか。
🦝 < ユーニックス家の食卓