Python における HTML/XML 解析ライブラリ「Beautiful Soup 4」徹底解説

Beautiful Soup 4 とは

Beautiful Soup は、Python 開発者が HTML や XML データから情報を抽出するために使用する強力なライブラリです。このツールは、文書木構造の作成、検索、修正を直感的な方法で行うことを可能にし、手動でのスクレイピングに費やす時間を大幅に削減します。

本ガイドでは、バージョン 4.12.0 を対象に、主要機能と使用法を解説します。また、例示コードは Python 3.8 環境で動作することを前提としています。以前のバージョンである BS3 はサポート終了しているため、新規プロジェクトでは必ず BS4 を採用してください。

初期設定とインストール

最新 OS ではパッケージマネージャを利用できますが、一般的には PyPI から pip で入手するのが推奨されます。

pip install beautifulsoup4

パーサー(解析エンジン)として標準のものに加え、外部ライブラリも選択可能です。速度やエラー耐性を考慮すると、以下のような選択肢があります。

  • html.parser: Python 標準内蔵。追加インストール不要だが性能は平凡。
  • lxml: C ライブラリベースで高速。XML モードもサポート。
  • html5lib: ブラウザに近い厳密な解析を行うが、処理速度は遅め。

lxml を導入する場合は以下のコマンドを実行します。

pip install lxml
パーサータイプ指定方法長所短所
組み込み"html.parser"標準搭載・安定パフォーマンスに限界あり
lxml"lxml"最速・高耐性C拡張依存
html5lib"html5lib"最も正確な構文解釈非常に重い

基本的なパース処理

まずはサンプル HTML を定義し、BeautifulSoup オブジェクトに変換する手順を確認しましょう。

# サンプルデータ
sample_html = """
<html><head><title>テストページ</title></head>
<body>
<p class="intro">これは挨拶文です。</p>
<a href="http://example.com/link1">リンク 1</a>
<a href="http://example.com/link2">リンク 2</a>
</body>
</html>
"""

from bs4 import BeautifulSoup

# 解析对象の生成
parser_obj = BeautifulSoup(sample_html, 'html.parser')

# 整形された出力確認
print(parser_obj.prettify())

このようにして構築されたオブジェクトは、DOM ツリーとして振る舞います。

オブジェクトの種類

内部では主に 4 つの要素クラスが存在します。

  1. Tag: HTML タグそのもの。.nameでタグ名にアクセス可能。
  2. NavigableString: タグ内のテキスト内容。
  3. Comment: コ멘트ブロック (<!-- -->) 専用の文字列サブクラス。
  4. BeautifulSoup: 文書全体を表すルートオブジェクト。

例:

element = parser_obj.title
print(element.name)      # 'title'
print(element.string)    # 'テストページ'

属性へのアクセスは辞書操作と同様です。

link_tag = parser_obj.a
url = link_tag['href']         # 'http://example.com/link1'
link_tag['target'] = '_blank'  # 属性の追加・更新

class 属性は複数値を持つ可能性があるため、リストとして返却されます。

ドツクメントツリーの走査

子孫ノードや兄弟ノードへの移動メソッドを利用することで、任意の位置へ遷移できます。

  • .contents: 直接の子要素をリストで取得。
  • .children: 直接の子要素をジェネレータでループ。
  • .descendants: 再帰的なすべての子孫を取得。
  • .parent, .parents: 親要素への参照。
  • .next_sibling, .previous_sibling: 同じ階層の隣接要素。

注意点として、空白を含んだテキストノード(空行など)も兄弟ノードとして扱われる場合があります。

検索機能の詳細

find()find_all() が代表的な検索メソッドです。条件に合致する最初の一要素、またはすべての要素を返します。

フィルター条件

以下のようなオブジェクトを第一引数に渡してフィルタリングできます。

  • 文字列: タグ名との完全一致。
  • 正規表現: タグ名のパターンマッチ。
  • リスト: マッチするいずれかのタグ名。
  • 関数: カスタムロジックによる評価。

例:クラス名を含むリンクを抽出

# クラス 'sister' を持つ a タグを検索
results = parser_obj.find_all("a", class_="sister")

# リスト内包表記を使用
urls = [node.get('href') for node in results if node.get('href')]

attrs 辞書を使えば、予約語ではない属性への検索も容易になります。

CSS セレクター

select() メソッドを使うと、Web ブラウザの開発者ツールで使用できるような CSS シntax で検索可能です。これにより複雑な条件も簡潔に記述できます。

# id が link1 の要素を取得
elem = parser_obj.select_one("#link1")

# class に sister を含む a タグ全件
anchors = parser_obj.select("a.sister")

ツリーの編集と書き換え

解析したツリーに対して、既存要素の変更や新規追加が可能です。

  • append() / insert(): 新しい内容を末端または指定位置に追加。
  • replace_with(): 該当ノードを別の要素と置き換える。
  • decompose(): ノードを破棄しメモリから完全に削除。
  • extract(): ノードをツリーから切り離すが、返り値として保持する。
  • unwrap(): タグ自体を消去し、中身だけを残す。

文字列置換については、直接 string へ代入することで上書き動作となります。

text_node = parser_obj.b.string
text_node.replace_with("新しいテキスト")

出力とエンコーディング

最終的にデータを文字列に戻す際は、以下のメソッドを活用します。

  • str(): UTF-8 エンコードされた HTML 文字列に変換。
  • encode(): 特定のバイト列(例:utf-8, latin-1)として出力。
  • get_text(): タグに含まれるテキストのみを連結して取得。

Beautiful Soup は内部的に Unicode を使用するため、元のファイルがどのようなエンコードであっても適切に変換されることが多くあります。ただし、明示的に from_encoding を指定すれば、推測プロセスを省略でき処理を安定させられます。

# テキスト情報のみ抽出
page_content = parser_obj.get_text(separator=' ', strip=True)

高度なカスタマイズ

SoupStrainer

大規模な HTML ファイルに対し、特定のタグのみを解析対象に絞り込むことで、メモリ消費と処理時間を最適化できます。

from bs4 import SoupStrainer

# 'img' タグのみをパースするよう指示
only_images = SoupStrainer('img')
filtered_soup = BeautifulSoup(large_html_source, 'html.parser', parse_only=only_images)

注意: この機能は一部のパーサー(特に html5lib)では無効になる可能性があります。

重複属性の制御

HTML 仕様では属性の重複は非推奨ですが、ブラウザは最後の値を採用することが多いです。Beautiful Soup でもこれを設定可能です。

タグ: beautiful-soup Python web-scraping html-parsing lxml

7月28日 22:07 投稿