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