ユーニックス総合研究所

  • home
  • archives
  • bs4-table

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>  

theadtbodyが組み込まれているのが特徴です。
このタグは省略することも可能です。

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>  

↑の構成はtheadtbodyが省略されているのが特徴です。
この記事では、これら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.Tagfind()メソッドは第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タグのテキストを行に保存します。
行を保存し終わったらテーブルに行を追加します。

最後に出力のためmatfor文で回します。
行列の行の列を','.join(r)で結合し、カンマ区切りのフォーマット、つまりCSVに変換します。

↑のコードの出力は↓のようになります。

商品名,在庫量,値段,販売先  
おいしいリンゴ,100,200円,スーパー池本  
赤いブドウ,200,300円,スーパー橋本  
小さいメロン,50,500円,田中商店  

2つ目のテーブルのパース

2つ目のテーブルのコードをhtml変数に保存しておきます。

html = '''  
<table>  
  <tr>  
    <th>商品名</th>  
    ~ 省略 ~   
'''  

2つ目のテーブルにはtheadtbodyがありません。
あるのはtable, tr, th, tdのみです。
そのため最初にtableタグを取得したらtrタグをまとめて取得します。
そしてマジックナンバーでtrのリストを参照し、商品名などを取得します。
残りのtrfor文で回し、行ごとに解析していきます。

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))  # カンマ(,)で列を結合して表示  

theadtbodyタグがある構成のテーブルと比べると、解析は多少推測みたいなものが入っていて、エラーの考慮などが必要になってきます。

↑のコードの出力結果↓。

商品名,在庫量,値段,販売先  
おいしいリンゴ,100,200円,スーパー池本  
赤いブドウ,200,300円,スーパー橋本  
小さいメロン,50,500円,田中商店  

おわりに

BeautifulSoup4を使うと簡単にテーブルをパースすることが可能です。
スクレイピングに使ってみられてはいかがでしょうか。

🦝 < ユーニックス家の食卓