Webスクレイピングを行っていると、ウェブサイト上の表(テーブル)データをPythonのプログラムで扱いたい場面が多々あります。
特にHTMLの<table>タグで構成されたデータは、適切な手順を踏むことで、Pythonの標準的なデータ構造であるリスト形式へ簡単に変換することが可能です。
この記事では、Beautiful Soupライブラリを使用して、HTMLテーブルを二次元リストへ効率的に変換する具体的な実装方法を解説します。
最新のPython環境でも動作する汎用性の高いコードを例に、データのクレンジングから応用的な抽出まで、実戦で役立つテクニックを確認していきましょう。
Beautiful Soupを使ったテーブル抽出の基本準備
まずは、HTML解析を行うために必要なライブラリの準備から始めましょう。
Pythonでスクレイピングを行う際は、HTMLを取得するためのrequestsと、解析を行うためのBeautiful Soup(beautifulsoup4)を組み合わせて使用するのが一般的です。
pip install beautifulsoup4 requests
インストールが完了したら、対象となるHTMLからテーブル要素を特定するコードを記述します。
Beautiful Soupでは、findメソッドやfind_allメソッドを用いることで、特定のタグを抽出できます。
スクレイピングを開始する前に、対象サイトの利用規約を確認し、サーバーに負荷をかけない適切な間隔でのアクセスを心がけてください。
HTMLテーブルを二次元リストに変換する基本コード
HTMLのテーブル構造は、行を表す<tr>タグと、セルを表す<th>または<td>タグで構成されています。
これらを順番にループ処理で取得し、リストの中へ格納していくことで、Pythonで扱いやすい二次元リストを作成できます。
以下に、最も基本的な変換プログラムの例を示します。
from bs4 import BeautifulSoup
# 解析対象のサンプルHTML
html_content = """
<table id="target_table">
<tr>
<th>商品名</th>
<th>価格</th>
<th>在庫</th>
</tr>
<tr>
<td>ノートPC</td>
<td>120,000</td>
<td>あり</td>
</tr>
<tr>
<td>マウス</td>
<td>3,500</td>
<td>なし</td>
</tr>
</table>
"""
soup = BeautifulSoup(html_content, 'html.parser')
table = soup.find('table', id='target_table')
# 二次元リストを格納する変数
data_list = []
# すべての行(tr)を取得する
rows = table.find_all('tr')
for row in rows:
# 1行の中にあるセル(th, td)をすべて取得
cols = row.find_all(['th', 'td'])
# テキストのみを抽出してリスト化
row_data = [col.get_text(strip=True) for col in cols]
data_list.append(row_data)
print(data_list)
[['商品名', '価格', '在庫'], ['ノートPC', '120,000', 'あり'], ['マウス', '3,500', 'なし']]
このコードでは、find_all([‘th’, ‘td’])と記述することで、ヘッダー行とデータ行を同時に取得しています。
get_text(strip=True)を使用することで、セル内の不要な改行や空白を除去した状態でリストに格納できるため、後のデータ処理がスムーズになります。
特定のクラスやIDを持つテーブルを指定して抽出する
実際のウェブサイトには複数のテーブルが存在することが多いため、特定のテーブルのみを指定して取得する必要があります。
Beautiful Soupでは、クラス名やIDを指定することで、目的の要素を絞り込むことが可能です。
# class属性を指定して抽出する場合
table = soup.find('table', class_='ranking-table')
# 複数の条件を指定する場合
table = soup.find('table', {'class': 'data-list', 'id': 'main-results'})
このように、特定の属性を利用して要素を絞り込むことで、意図しないデータの混入を防ぐことができます。
また、テーブルにヘッダー(thead)とボディ(tbody)が明確に分かれている場合は、それぞれのタグを明示的に指定するとより確実です。
ヘッダーとデータを分離して取得する方法
二次元リストを作成した後、最初の1行目をカラム名(ヘッダー)、それ以降をデータとして別に扱いたい場合も多くあります。
その場合は、スライス機能を利用するか、findメソッドを使い分けることで対応できます。
# ヘッダーのみを抽出
headers = [th.get_text(strip=True) for th in table.find_all('th')]
# データ行のみを抽出(tr内のtdがある行のみ)
rows = table.find_all('tr')
records = []
for row in rows:
cells = row.find_all('td')
if cells:
records.append([cell.get_text(strip=True) for cell in cells])
print("ヘッダー:", headers)
print("データ:", records)
if cells:という条件式を入れることで、空の行やヘッダーのみの行を除外する工夫が重要です。
リスト内包表記を用いた高度でシンプルな記述
Pythonのリスト内包表記を活用すると、二次元リストへの変換をわずか数行で記述することができます。
コードの可読性を保ちつつ、記述量を減らしたい場合に非常に有効なテクニックです。
rows = table.find_all('tr')
# 1行で全テーブルデータを二次元リスト化
matrix = [[c.get_text(strip=True) for c in r.find_all(['th', 'td'])] for r in rows]
この記述方法は、データ解析の現場で頻繁に利用されるため、覚えておくと開発効率が飛躍的に向上します。
複雑なテーブル構造(colspan/rowspan)への対応
ウェブサイトによっては、セルの結合(colspanやrowspan)が使われている複雑なテーブルが存在します。
Beautiful Soup単体での単純なfind_allでは、結合されたセルの情報を正確に二次元リストのインデックスへ反映させることは困難です。
このような場合は、Pandasライブラリのread_html関数の利用を検討してください。
Pandasは内部でBeautiful Soupやlxmlを使用しており、複雑な結合セルも自動的に補完してデータフレーム形式へ変換してくれます。
import pandas as pd
# HTML文字列から直接テーブルをリスト(データフレームのリスト)として読み込む
dfs = pd.read_html(html_content)
# 最初のテーブルをリストに変換
data_list = dfs[0].values.tolist()
スクレイピングの目的に応じて、Beautiful Soupによる細かな制御と、Pandasによる効率的な処理を使い分けるのがプロのテクニックです。
まとめ
PythonのBeautiful Soupを使用すれば、HTMLのテーブルデータをリスト形式へ変換する作業は非常にシンプルに行えます。
基本的にはfind_all('tr')で行を取得し、その中でさらにセル要素を抽出してリストへ追加していく流れとなります。
データのクレンジングにはget_text(strip=True)を活用し、特定のテーブルを狙う際はクラス名やIDによる絞り込みを徹底しましょう。
今回紹介した手法をマスターすることで、Web上のあらゆる情報を効率的に収集し、自社のシステムや分析ツールで活用できるようになります。
まずは手近なサイトのテーブル抽出から挑戦し、Pythonによるスクレイピングの利便性を体感してみてください。
