Pythonを用いたWebスクレイピングにおいて、HTMLの中から特定の情報を取り出す作業は、最も基本的かつ重要なプロセスです。
その中心的な役割を担うライブラリがBeautifulSoupであり、特に「タグ名」をキーにした要素の指定は、スクレイピングの第一歩といえます。
HTMLは階層構造になっており、各データは
といったタグの中に格納されています。これらのタグを正確に指定し、必要なデータだけを効率的に抽出するスキルは、データ分析や業務自動化の現場で欠かせません。本記事では、BeautifulSoupでタグ名を指定して要素を抽出する基本から、実戦で役立つ高度なテクニックまでを詳しく解説します。
BeautifulSoupの基本とタグ指定の重要性
BeautifulSoupは、HTMLやXMLファイルを解析(パース)し、プログラムから扱いやすいツリー構造に変換するライブラリです。Webサイトの構造を理解し、特定のデータをピンポイントで取得するためには、このツリー構造の中から目的のタグを見つけ出す必要があります。
タグ名の指定は、スクレイピングにおいて最も直感的な方法です。例えば、記事のタイトルを取得したいなら
タグを、リンクの一覧が欲しいならタグを探すことになります。しかし、実際のWebサイトは複雑にネスト(入れ子)されており、単純にタグ名だけで指定すると、不要な情報まで取得してしまうことがあります。そのため、find()やfind_all()といったメソッドの特性を深く理解し、使い分けることが重要です。
find()メソッドによる単一要素の抽出
find()メソッドは、HTML内を検索し、条件に合致する最初の要素を1つだけ返します。ページ内に同じタグが複数ある場合でも、最初に見つかったものだけを取得するため、IDのようにページ内で一意であるべき要素や、構造上最初に出現することがわかっている要素の抽出に適しています。
基本的な使い方
まずは、最もシンプルなタグ名のみを指定する方法を見てみましょう。
from bs4 import BeautifulSoup
# サンプルHTML
html_content = """
<html>
<body>
<h1>プログラミング講座</h1>
<p>Pythonの学習を始めましょう。</p>
<p>BeautifulSoupは便利です。</p>
</body>
</html>
"""
# BeautifulSoupオブジェクトの作成
soup = BeautifulSoup(html_content, 'html.parser')
# h1タグを取得
first_h1 = soup.find('h1')
# pタグを取得(最初に見つかったもの)
first_p = soup.find('p')
print(f"h1タグの内容: {first_h1.text}")
print(f"最初のpタグの内容: {first_p.text}")
実行結果:
h1タグの内容: プログラミング講座
最初のpタグの内容: Pythonの学習を始めましょう。
このように、soup.find('タグ名')と記述するだけで、そのタグの内容を抽出できます。.text属性を使用することで、タグ内のテキスト部分だけを取り出すことが可能です。
存在しないタグを指定した場合の挙動
スクレイピングを行う際、指定したタグが必ずしもページ内に存在するとは限りません。存在しないタグをfind()で指定した場合、エラーにはならずNoneが返されます。
# 存在しないタグを検索
non_existent = soup.find('span')
if non_existent is None:
print("指定したタグは見つかりませんでした。")
else:
print(non_existent.text)
実行結果:
指定したタグは見つかりませんでした。
Noneに対して.text属性などを呼び出そうとするとエラー(AttributeError)が発生するため、実戦的なコードでは、必ず戻り値がNoneでないかチェックする処理を入れるのがベストプラクティスです。
find_all()メソッドによる複数要素の抽出
ページ内にある特定のタグをすべて取得したい場合は、find_all()メソッドを使用します。このメソッドは、条件に合致するすべての要素をリスト形式で返します。
リスト形式での取得とループ処理
リンク一覧や商品リストの取得など、同じ構造が繰り返される箇所で威力を発揮します。
html_list = """
<ul>
<li>Python</li>
<li>Java</li>
<li>C++</li>
<li>Ruby</li>
</ul>
"""
soup = BeautifulSoup(html_list, 'html.parser')
# すべてのliタグを取得
items = soup.find_all('li')
print(f"取得した要素数: {len(items)}")
for item in items:
print(f"言語名: {item.text}")
実行結果:
取得した要素数: 4
言語名: Python
言語名: Java
言語名: C++
言語名: Ruby
find_all()の戻り値はイテレータとして扱えるため、for文を使って各要素に順番にアクセスできます。なお、合致する要素が1つもない場合、find_all()は空のリスト[]を返します。
取得件数の制限(limit)
大量のデータがあるページで、上位数件だけが必要な場合は、limit引数を使用することで取得数を制限できます。
# 最初の2つだけ取得
top_items = soup.find_all('li', limit=2)
for item in top_items:
print(item.text)
実行結果:
Python
Java
この引数を使うことで、メモリの節約や処理速度の向上が期待できます。
タグ名指定の応用テクニック
単純なタグ名の指定だけでなく、BeautifulSoupにはより柔軟な指定方法が用意されています。
複数のタグを同時に指定する
「h1タグとh2タグの両方を取得したい」という場合は、タグ名をリスト形式で渡します。
html_multi = """
<div>
<h1>主要な言語</h1>
<p>解説文です。</p>
<h2>スクリプト言語</h2>
<p>Pythonなどがあります。</p>
</div>
"""
soup = BeautifulSoup(html_multi, 'html.parser')
# h1とh2をまとめて取得
headers = soup.find_all(['h1', 'h2'])
for header in headers:
print(f"見出し: {header.text}")
実行結果:
見出し: 主要な言語
見出し: スクリプト言語
このように、['h1', 'h2']と記述することで、OR条件での抽出が可能になります。
属性(class, id)と組み合わせた絞り込み
タグ名だけでは範囲が広すぎる場合、classやidなどの属性を併用して絞り込みます。
html_attr = """
<div>
<p class="title">ニュース1</p>
<p class="content">内容の詳細...</p>
<p class="title">ニュース2</p>
</div>
"""
soup = BeautifulSoup(html_attr, 'html.parser')
# classが"title"であるpタグのみを取得
titles = soup.find_all('p', class_='title')
for t in titles:
print(f"タイトル: {t.text}")
実行結果:
タイトル: ニュース1
タイトル:ニュース2
Pythonの予約語であるclassと衝突を避けるため、class_(アンダースコア付き)を使用するのがBeautifulSoupのルールです。
正規表現を用いた柔軟なマッチング
タグ名の一部が動的に変わる場合や、特定のパターンを持つタグを探したい場合は、Pythonのreモジュール(正規表現)を利用できます。
import re
html_re = """
<div>
<h1 id="main-header">メイン</h1>
<h2 id="sub-header">サブ</h2>
<section>セクション</section>
</div>
"""
soup = BeautifulSoup(html_re, 'html.parser')
# 'h'で始まるタグ(h1, h2など)をすべて取得
header_tags = soup.find_all(re.compile("^h[1-6]"))
for tag in header_tags:
print(f"見つかったタグ: {tag.name}")
実行結果:
見つかったタグ: h1
見つかったタグ: h2
re.compile()を引数に渡すことで、「特定の文字列で始まるタグ」や「特定の数値を含むタグ」といった高度な指定が可能になります。
select()とselect_one()によるCSSセレクタの活用
タグ名の指定において、Web開発者に馴染みのある「CSSセレクタ」を使用する方法もあります。それがselect()(複数)とselect_one()(単一)です。
find()メソッドは、HTML内を検索し、条件に合致する最初の要素を1つだけ返します。ページ内に同じタグが複数ある場合でも、最初に見つかったものだけを取得するため、IDのようにページ内で一意であるべき要素や、構造上最初に出現することがわかっている要素の抽出に適しています。from bs4 import BeautifulSoup
# サンプルHTML
html_content = """
<html>
<body>
<h1>プログラミング講座</h1>
<p>Pythonの学習を始めましょう。</p>
<p>BeautifulSoupは便利です。</p>
</body>
</html>
"""
# BeautifulSoupオブジェクトの作成
soup = BeautifulSoup(html_content, 'html.parser')
# h1タグを取得
first_h1 = soup.find('h1')
# pタグを取得(最初に見つかったもの)
first_p = soup.find('p')
print(f"h1タグの内容: {first_h1.text}")
print(f"最初のpタグの内容: {first_p.text}")
h1タグの内容: プログラミング講座
最初のpタグの内容: Pythonの学習を始めましょう。
soup.find('タグ名')と記述するだけで、そのタグの内容を抽出できます。.text属性を使用することで、タグ内のテキスト部分だけを取り出すことが可能です。find()で指定した場合、エラーにはならずNoneが返されます。# 存在しないタグを検索
non_existent = soup.find('span')
if non_existent is None:
print("指定したタグは見つかりませんでした。")
else:
print(non_existent.text)
指定したタグは見つかりませんでした。
Noneでないかチェックする処理を入れるのがベストプラクティスです。find_all()メソッドを使用します。このメソッドは、条件に合致するすべての要素をリスト形式で返します。html_list = """
<ul>
<li>Python</li>
<li>Java</li>
<li>C++</li>
<li>Ruby</li>
</ul>
"""
soup = BeautifulSoup(html_list, 'html.parser')
# すべてのliタグを取得
items = soup.find_all('li')
print(f"取得した要素数: {len(items)}")
for item in items:
print(f"言語名: {item.text}")
取得した要素数: 4
言語名: Python
言語名: Java
言語名: C++
言語名: Ruby
find_all()の戻り値はイテレータとして扱えるため、for文を使って各要素に順番にアクセスできます。なお、合致する要素が1つもない場合、find_all()は空のリスト[]を返します。limit引数を使用することで取得数を制限できます。# 最初の2つだけ取得
top_items = soup.find_all('li', limit=2)
for item in top_items:
print(item.text)
Python
Java
html_multi = """
<div>
<h1>主要な言語</h1>
<p>解説文です。</p>
<h2>スクリプト言語</h2>
<p>Pythonなどがあります。</p>
</div>
"""
soup = BeautifulSoup(html_multi, 'html.parser')
# h1とh2をまとめて取得
headers = soup.find_all(['h1', 'h2'])
for header in headers:
print(f"見出し: {header.text}")
見出し: 主要な言語
見出し: スクリプト言語
['h1', 'h2']と記述することで、OR条件での抽出が可能になります。html_attr = """
<div>
<p class="title">ニュース1</p>
<p class="content">内容の詳細...</p>
<p class="title">ニュース2</p>
</div>
"""
soup = BeautifulSoup(html_attr, 'html.parser')
# classが"title"であるpタグのみを取得
titles = soup.find_all('p', class_='title')
for t in titles:
print(f"タイトル: {t.text}")
タイトル: ニュース1
タイトル:ニュース2
classと衝突を避けるため、class_(アンダースコア付き)を使用するのがBeautifulSoupのルールです。reモジュール(正規表現)を利用できます。import re
html_re = """
<div>
<h1 id="main-header">メイン</h1>
<h2 id="sub-header">サブ</h2>
<section>セクション</section>
</div>
"""
soup = BeautifulSoup(html_re, 'html.parser')
# 'h'で始まるタグ(h1, h2など)をすべて取得
header_tags = soup.find_all(re.compile("^h[1-6]"))
for tag in header_tags:
print(f"見つかったタグ: {tag.name}")
見つかったタグ: h1
見つかったタグ: h2
re.compile()を引数に渡すことで、「特定の文字列で始まるタグ」や「特定の数値を含むタグ」といった高度な指定が可能になります。select()(複数)とselect_one()(単一)です。| メソッド | 対応する基本メソッド | 特徴 |
|---|---|---|
select\_one('tag') |
find('tag') |
CSSセレクタ形式で1つ抽出 |
select('tag') |
find\_all('tag') |
CSSセレクタ形式でリスト抽出 |
CSSセレクタを使えば、div > p(divの直下のp)やdiv p(div配下のすべてのp)といった階層構造を直感的に記述できます。
html_css = """
<div id="container">
<div class="article">
<h2>記事タイトル</h2>
<p>本文です。</p>
</div>
</div>
"""
soup = BeautifulSoup(html_css, 'html.parser')
# 階層を指定して取得
title = soup.select_one('#container .article h2')
print(title.text)
実行結果:
記事タイトル
複雑な構造のサイトでは、find()を何度も重ねるより、select()で一気にパスを指定したほうがコードの可読性が高まる場合があります。
実戦的なスクレイピングの注意点とベストプラクティス
タグ名指定で要素を抽出する際には、いくつか注意すべき実務上のポイントがあります。
第一に、HTMLの構造変化への耐性です。タグ名だけで指定していると、サイトのデザインリニューアルによって
第二に、タグのネストの考慮です。特定の
# 悪い例:ページ全体のすべてのpタグを拾ってしまう
# all_p = soup.find_all('p')
# 良い例:特定の範囲に絞ってから検索する
main_content = soup.find('main')
if main_content:
target_paragraphs = main_content.find_all('p')
このように「親要素を見つける」→「その中で子要素を探す」というステップを踏むことで、意図しないデータの混入を防ぐことができます。
また、Webサイトへの負荷にも配慮が必要です。タグ名を指定して大量のデータを抽出する場合、ループの中でリクエストを送りすぎないよう、適切なスリープ処理(time.sleep)を入れるのがエチケットです。
まとめ
PythonのBeautifulSoupを用いたタグ名指定による要素抽出は、スクレイピングの基本にして究極のテクニックです。
- find():最初に見つかった1つの要素を取得する。
- find_all():合致するすべての要素をリストで取得する。
- 属性との組み合わせ:class_やidを使って精度を高める。
- 正規表現・CSSセレクタ:より柔軟で直感的な指定を行う。
これらの手法を状況に応じて使い分けることで、複雑なWebサイトからでも必要な情報を正確に、そして効率的に抜き出すことが可能になります。まずは単純なタグ指定から始め、徐々に属性や階層を意識した抽出にステップアップしていきましょう。
スクレイピングの実装において、タグをどう指定するかは「データの地図」を描く作業に似ています。本記事で紹介したテクニックを活用し、ぜひ精度の高いデータ収集を実現してください。
