PythonでWebスクレイピングを行う際、目的のデータを正確に抽出することは最も重要な工程の一つです。
Webサイトの構造が複雑化している現代では、一つのHTMLタグに複数のCSSクラスが割り当てられているケースが珍しくありません。
特定のクラスを持つ要素だけを絞り込みたい場合、単純な検索ではなく「クラスAとクラスBの両方を持つ」というAND検索のテクニックが必要になります。
Beautiful Soupには複数の検索手法が用意されていますが、記述方法によって動作や柔軟性が大きく異なります。
本記事では、複数クラスを指定したAND検索について、主要な2つのメソッドであるfind_allとselectの使い分けを中心に詳しく紹介します。
複数クラスを指定したAND検索が必要な理由
現代のフロントエンド開発において、Tailwind CSSやBootstrapといったCSSフレームワークの普及により、HTMLタグには多くのクラスが付与されるようになりました。
例えば、単にclass="item"という指定だけでは、広告パーツや関連記事など、本来取得したくない要素までヒットしてしまうことがあります。
そこで、「商品リストの中にある(item)」「現在セール中の(sale)」という2つの条件を満たす要素のみを抽出するといった絞り込みが不可欠です。
AND検索をマスターすることで、ノイズの少ない綺麗なデータを効率的に収集できるようになります。
また、スクレイピングのコードがシンプルになり、後からのメンテナンス性も向上するというメリットがあります。
find_allメソッドで複数クラスをAND検索する方法
Beautiful Soupの最も基本的なメソッドであるfind_allでも、複数クラスのAND検索は可能です。
ただし、記述方法を誤ると「いずれかのクラスを含む(OR検索)」になってしまうため注意が必要です。
attrs引数にリストを指定する場合の注意点
find_allの第二引数やclass_引数にリストを渡すと、デフォルトではOR検索(いずれかのクラスに合致)として処理されます。
以下のコードは、AND検索ではなくOR検索の結果を返します。
from bs4 import BeautifulSoup
html = """
<div class="content active">ターゲット</div>
<div class="content">対象外1</div>
<div class="active">対象外2</div>
"""
soup = BeautifulSoup(html, "html.parser")
# これはOR検索になるため、3つのdivすべてが取得される可能性があります
elements = soup.find_all("div", class_=["content", "active"])
for el in elements:
print(el.text)
ターゲット
対象外1
対象外2
文字列として完全に一致させる方法
HTML内のクラスの並び順が固定されている場合は、文字列として複数クラスを記述することでAND検索のような挙動を実現できます。
# クラスの順序が完全に一致している必要がある
elements = soup.find_all("div", class_="content active")
しかし、この方法はクラスの順番が入れ替わったり、間に別のクラスが入ったりすると抽出に失敗するため、堅牢なスクレイピングとは言えません。
関数(ラムダ式)を利用した厳格なAND検索
find_allで確実なAND検索を行うには、要素を判定する関数を引数に渡す方法が有効です。
# contentとactiveの両方を保持している要素のみを抽出
elements = soup.find_all(lambda tag: tag.name == "div" and
set(["content", "active"]).issubset(tag.get("class", [])))
for el in elements:
print(el.text)
ターゲット
この方法であれば、クラスの順番に関わらず、指定したすべてのクラスが含まれている要素だけを正確に取得できます。
selectメソッド(CSSセレクタ)によるAND検索
Beautiful Soupにおいて、複数クラスのAND検索を行う最も推奨される方法はselectメソッドの使用です。
selectメソッドはCSSセレクタを利用するため、Web制作に馴染みのある直感的な記述が可能です。
ドット繋ぎによるAND検索の記述
CSSのルールに従い、クラス名の先頭にドット(.)を付け、それらをスペースを入れずに連結させることでAND条件となります。
例えば、contentクラスとactiveクラスの両方を持つ要素を探す場合は、.content.activeと記述します。
from bs4 import BeautifulSoup
html = """
<div class="item sale primary">セール対象</div>
<div class="item primary">通常商品</div>
<div class="sale">バナーのみ</div>
"""
soup = BeautifulSoup(html, "html.parser")
# itemクラスとsaleクラスを両方持つ要素を抽出
results = soup.select(".item.sale")
for res in results:
print(res.text)
セール対象
この記述方法の優れた点は、HTML上のクラスの順序に依存しないことです。
class="item sale"であってもclass="sale item"であっても、正しくヒットします。
タグ名との組み合わせ
特定のタグに限定してAND検索を行いたい場合は、タグ名の直後にクラスを繋げます。
# divタグであり、かつitemとsaleの両方のクラスを持つ
results = soup.select("div.item.sale")
これにより、同じクラス名を持つ他のタグ(例えばspanタグなど)を除外して抽出することが可能になります。
find_allとselectの使い分け比較
どちらのメソッドを使用すべきか迷った際のために、それぞれの特徴を比較表にまとめました。
| 比較項目 | find_all | select (CSSセレクタ) |
|---|---|---|
| AND検索の書きやすさ | 関数定義が必要でやや複雑 | 非常に直感的で簡単 |
| クラスの順序依存 | 文字列指定時は依存する | 依存しない |
| 処理速度 | わずかに高速な傾向がある | find_allに比べると僅かに遅い |
| 柔軟性 | Pythonのロジックを組み込みやすい | CSSの高度な指定が可能 |
結論として、複数クラスのAND検索が目的であれば、selectメソッドを使用するのがベストプラクティスと言えます。
コードの可読性が高く、CSSの知識をそのまま転用できるため、開発効率が劇的に向上します。
実践的なコード例:多条件でのフィルタリング
より実用的なシーンとして、複雑な条件を組み合わせたスクレイピングの例を見ていきましょう。
ここでは、特定のカテゴリに属し、かつ在庫がある商品のみを抽出するコードを想定します。
html_doc = """
<ul id="product-list">
<li class="product electronics available">スマートフォン - 在庫あり</li>
<li class="product electronics soldout">ノートPC - 売り切れ</li>
<li class="product books available">Python教本 - 在庫あり</li>
<li class="ad-banner electronics">広告:最新ガジェット</li>
</ul>
"""
soup = BeautifulSoup(html_doc, "html.parser")
# 1. electronicsカテゴリで在庫がある(available)商品(product)のみをAND検索
target_products = soup.select("li.product.electronics.available")
print("--- 抽出結果 ---")
for product in target_products:
print(product.get_text())
--- 抽出結果 ---
スマートフォン - 在庫あり
このように、3つ以上のクラスが絡む場合でも、ドットで繋ぐだけで簡単に絞り込みが可能です。
また、親要素のIDと組み合わせることで、特定のエリア内にある要素のみを対象とするさらに精密な抽出も行えます。
# product-listというIDの中にある、特定クラスの要素を探す
results = soup.select("#product-list .product.available")
複数クラス検索でよくある落とし穴と対処法
実装時に陥りやすいミスとその解決策についても知っておく必要があります。
クラス名のスペルミスと動的クラス
スクレイピング対象のサイトがReactやVue.jsなどのフレームワークで構築されている場合、クラス名が動的に生成されることがあります。
例えば、jsc-12345のようなランダムな文字列が含まれるクラスは、再読み込みのたびに変わる可能性があります。
このような場合は、AND検索に含めるクラスを「不変なもの」だけに絞るか、部分一致(正規表現)を検討してください。
スペースの有無による挙動の違い
select(".class1 .class2")のようにドットの間にスペースを入れてしまうと、「class1を持つ要素の子孫にあるclass2を持つ要素」という意味に変わってしまいます。
同一タグ内の複数クラスを指す場合は、必ず.class1.class2とスペースなしで記述することを徹底してください。
Beautiful Soupのバージョンによる差異
非常に古いバージョンのBeautiful Soupを使用している場合、CSSセレクタのサポートが不完全なことがあります。
2026年現在のモダンな環境であれば問題ありませんが、環境構築時には最新のbeautifulsoup4がインストールされているか確認しましょう。
まとめ
PythonのBeautiful Soupを用いて複数クラスをAND検索する方法について解説しました。
find_allメソッドでもラムダ式を活用すればAND検索は可能ですが、より簡潔で直感的に記述できるselectメソッド(CSSセレクタ)の利用が最も効率的です。
.class1.class2のようにドットで繋ぐ記述法を覚えるだけで、複雑なHTML構造から目的のデータをピンポイントで抽出できるようになります。
スクレイピングの精度を高めるためには、対象サイトのHTMLを事前によく観察し、どのクラスの組み合わせがユニーク(一意)であるかを見極めることが成功の鍵となります。
今回紹介した手法を組み合わせて、ぜひ高度なデータ収集に役立ててください。
