表形式データの抽出と Pandas への変換 ― Beautiful Soup 応用ガイド
1. 全体像
-
HTML テーブルの特定
CSS セレクタや属性検索で<table>要素を取得します。ページに複数テーブルがある場合は、idやclassでフィルタリングすると意図した表だけを対象にできます。 -
ヘッダー解析
<th>タグから列名を取得します。rowspan・colspanがある場合はツリー構造をたどって論理的ヘッダーを構築します。 -
ボディ行の走査
<tr>→<td>の順に値を抽出し、ネストしたタグ(<a>など)があれば.get_text(strip=True)でテキストだけを取り出します。 -
Python リスト/辞書に整形
1 行=1 レコードのリストや辞書リストを作成し、欠損セルにはNoneを挿入。 -
Pandas DataFrame へ変換
pd.DataFrame(records, columns=headers)で即座に表形式データを扱えるようになります。 -
後処理
型変換・欠損値補完・重複除去・列名正規化などを行い、CSV や DB に保存します。
2. 具体的なコード例
ポイント補足
| 課題 | 解決策 |
|---|---|
<th> が複数行にまたがる複合ヘッダー |
pandas.read_html() で header=[0,1] とするか、Beautiful Soup で各 <th> の rowspan/colspan を再帰的に展開してフラット化 |
| セルに改行・空白が多い | get_text(" ", strip=True) の第1引数に区切り文字を指定し、改行をスペース1つへ統一 |
| 画像・アイコンが埋め込まれている | <img> の alt 属性を取得するか、['src'] をダウンロード URL として扱う |
| 数値にカンマが含まれる | str.replace(",", "") → pd.to_numeric() |
| 日付文字列の統一 | pd.to_datetime(df["日付列"], format="%Y/%m/%d") などで ISO 形式へ正規化 |
3. pandas.read_html() と組み合わせる場合
pandas.read_html() と組み合わせる場合-
単純な表:
df_list = pd.read_html(url, flavor="bs4", match="売上表")で Beautiful Soup パーサを内部利用しつつ自動 DataFrame 化。 -
複雑な表: 先に Beautiful Soup で DOM を操作して不要列や広告行を削除後、
pd.read_html(str(clean_table))に渡すと手間を減らせます。
4. ベストプラクティス
| 実践的アドバイス | 説明 |
|---|---|
| テーブル検出ロジックは疎結合に | soup.select("table.sales") のようにセレクタに依存しすぎない書き方にして、HTML 構造が変わっても影響範囲を限定。 |
| 構造変化テストを自動化 | pytest + Beautiful Soup で「ヘッダー数が変わらないか」などをチェックし、サイト改修時の事故を減らす。 |
| 正規表現で数値抽出 | re.sub("[^0-9.-]", "", cell) で通貨記号や単位を除去してから数値変換。 |
| 文字エンコーディング対策 | response.encoding を確認し、response.apparent_encoding で推定結果も検証。Beautiful Soup の from_encoding 引数で強制指定可能。 |
| パフォーマンス改善 | テーブルが大きい場合、records.append ではなくリスト内包表記+pd.DataFrame.from_records を使うと高速。 |
5. まとめ
-
Beautiful Soup で テーブル構造を柔軟に解析 → 行列データを Python のリスト/辞書にマッピング → Pandas で高機能なデータ処理を実施、という分業が最も保守性と拡張性を両立します。
-
pandas.read_html()は便利ですが、複雑なヘッダー や 動的に生成されるセル を扱う際には Beautiful Soup との併用が必須です。 -
型変換・欠損処理・エンコーディング管理・テスト自動化を早期に組み込み、将来の HTML 変更やデータ品質問題に強いスクレイピング基盤を構築しましょう。
ChatGPT4o 生成日:2025/06/19