1. find
とfind_all
を使う理由は?
今日は、HTMLドキュメントから効率的にデータを抽出するための2つの基本メソッド、find
とfind_all
について説明するね。
コードに入る前に、そもそもなぜこれらのメソッドが必要なのか考えてみよう。ウェブページを巨大な図書館としてイメージしてみて。それぞれのHTML要素が本棚の中の本みたいなものだよね。必要な情報を探すのって、味のわからないアイスクリームを選ぶような感じになっちゃう。そこで登場するのがfind
とfind_all
という「探知機」だ!
find
: これは朝一番でコーヒーを手に取るみたいなメソッド。条件に合った最初の要素を素早く見つけてくれるよ。find_all
: こっちはもっとじっくり構えるタイプ。同じ条件に合致するすべての要素をリストで返してくれるよ。複数の情報(たとえばコーヒーを一日に数杯飲むみたいに)が必要なときに便利だね。
2. find
の使い方
find
は、一番最初に条件に合致する要素を1つだけ取得したいときに使えるよ。このメソッドはタグの名前や属性、関数なんかをパラメータとして受け取ることができるんだ。
find
メソッドのシグネチャ
find(name=None, attrs={}, recursive=True, string=None, **kwargs)
find
メソッドのパラメータ
- name: 探したいタグの名前。たとえば、
div
、p
、h1
、a
など、どんなHTMLタグでも指定できるよ。 - attrs: タグの属性を指定する辞書。例:
{'class': 'example'}
や{'id': 'main'}
。検索をさらに絞り込むときに使えるよ。 - recursive: 再帰的に(つまり、ネストされた要素も含めて)検索するかどうかを決めるブール値。デフォルトは
True
。 - string: 特定のテキストを含む要素を検索するのに便利だよ。
- kwargs: 属性検索用の追加引数。たとえば
class_
を指定すると、自動的にattrs={'class': 'value'}
として扱われるよ。
例
from bs4 import BeautifulSoup
html_doc = """
<html>
<head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
</body>
</html>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
first_link = soup.find('a') # 最初の<a>タグを見つける
print(first_link) # 出力: <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>
見ての通り、find
メソッドは最初の<a>
タグを見つけたよ。これで必要な情報を見つける道筋ができたね。
3. find_all
の使い方
find_all
は、条件に合致するすべての要素をリストで返してくれるメソッドだよ。同じタイプのタグを全部取得したり、特定のクラスを持つ要素を全部取得したいときに特に便利だね。
find_all
メソッドのシグネチャ
find_all(name=None, attrs={}, recursive=True, string=None, limit=None, **kwargs)
find_all
メソッドのパラメータ
- name: 探索するタグの名前。たとえば
div
やa
、またはリストで["div", "p"]
みたいに複数指定もできるよ。 - attrs: 属性フィルタリング用の辞書。例:
{'class': 'example'}
。 - recursive: ネストされたタグも含めて検索するかどうか。デフォルトは
True
だよ。 - string: 指定したテキストを含むタグを探す。
- limit: 戻り値の数を制限する。指定すると、最大
limit
個だけ返すよ。 - kwargs: タグの属性フィルタリング用の追加パラメータ。
例: find_all
の使い方
もしfind
が本棚から特定の本を見つける速攻方法だとしたら、find_all
は棚全体をじっくり読み込むような方法。
all_links = soup.find_all('a') # 全ての<a>タグを見つける
for link in all_links:
print(link.get('href')) # 出力: http://example.com/elsie, http://example.com/lacie, http://example.com/tillie
この例では、全ての<a>
タグを見つけて、それぞれのリンクを取得したよ。ウェブページの全てのリンクを集めたいときに便利だね。
注意! find()
やfind_all()
メソッドは、soup
オブジェクトだけでなく、メソッドで見つかった子要素にも使えるよ。
4. 特定の属性を使ったフィルタリング
次に、メソッドfind
やfind_all
を使って、属性でフィルタリングする方法を紹介するね。これはコーヒーマシンでフィルターを設定するのと同じようなもの。自分好みの情報を効率よく引き出せるんだ。
link_with_id = soup.find('a', id='link2') # id='link2'の<a>タグを見つける
print(link_with_id.text) # 出力: Lacie
例のように、id
パラメータを使うことで、目的の要素を素早く見つけることができたね。他の属性(たとえばclass
)も同じように使えるよ。
links_with_class = soup.find_all('a', class_='sister') # class='sister'の<a>タグを全部見つける
for link in links_with_class:
print(link.get('id')) # 出力: link1, link2, link3
5. find
とfind_all
の使い分けは?
両方のメソッドを知ったところで、「いつfind
を使って、いつfind_all
を使うべきか?」という疑問があるかもね。答えはシンプル。ページに必要な要素が1つしかない場合や最初の要素だけが必要な場合はfind
を使おう。逆に条件に合う要素が複数ある場合や全部集めたい場合はfind_all
を使うのがいいよ。
パラメータ | find |
find_all |
---|---|---|
戻り値 | 最初に見つかった要素(見つからない場合はNone ) |
見つかった要素のリスト(または空のリスト) |
用途 | 唯一または最初の要素を探す場合 | 条件に合うすべての要素を探す場合 |
パラメータlimit |
適用なし | サポート:最大数を設定可能 |
例: ページ内のすべてのh2
要素を取得したければfind_all
を使おう。でも最初のh2
だけでいいならfind
で十分だよ。
# 全てのh2タグを取得
all_h2_tags = soup.find_all("h2")
# 最初のh2タグを取得
first_h2_tag = soup.find("h2")
6. 実践課題
理論を学んだところで、実際にコードを書いてみよう。ここでは、ブログの記事タイトルとリンクを抽出する簡単なスクリプトを作るよ。find_all
を使って、<h2>
タグ内のリンクを探してみよう。
blog_html = """
<html>
<body>
<h2 class="post-title"><a href="http://example.com/post1">最初の記事</a></h2>
<h2 class="post-title"><a href="http://example.com/post2">2番目の記事</a></h2>
<h2 class="post-title"><a href="http://example.com/post3">3番目の記事</a></h2>
</body>
</html>
"""
blog_soup = BeautifulSoup(blog_html, 'html.parser')
post_titles = blog_soup.find_all('h2', class_='post-title')
for post in post_titles:
title = post.text
link = post.find('a')['href']
print(f"タイトル: {title}, リンク: {link}")
実行すると、以下のような出力が得られるはず!
タイトル: 最初の記事, リンク: http://example.com/post1
タイトル: 2番目の記事, リンク: http://example.com/post2
タイトル: 3番目の記事, リンク: http://example.com/post3
この例では、post
オブジェクトに対してfind()
を使っているんだ。こうすれば、既に見つかった要素の子要素をさらに探索できるよ。
7. よくあるミス
find
やfind_all
を使っていると、いくつかのよくあるミスに出会うことがあるよ。たとえば、見つからない要素にfind
を使うとNone
が返るけど、その後にメソッドを呼び出すとAttributeError
になっちゃう。だから常に結果をチェックするのが大事だね。
また、属性名のタイプミスも問題になるよ。HTMLコードをしっかり確認して間違いがないかチェックしよう!
GO TO FULL VERSION