BeautifulSoup4のfind_allでタグを全てぶんまわす
- 作成日: 2020-11-19
- 更新日: 2023-12-24
- カテゴリ: BeautifulSoup4
BeautifulSoup4のfind_allの使い方
プログラミング言語のPython(パイソン)にはHTML/XMLをパースする外部ライブラリがあります。
その名も「BeautifulSoup4(ビューティフル・スープ・フォー)」です。
BeautifulSoup4は指定したタグを検索してすべて取得するfind_all(ファインド・オール)
メソッドを持っています。
このメソッドを使うと、指定したタグをまとめて取得し、for
文などでタグを回すことが可能です。
この記事ではBeautifulSoup4のfind_all()
メソッドの使い方を詳しく解説します。
具体的には↓を見ていきます。
- find_allの構造
- タグ名を指定して要素を全て得る
- 複数のタグ名を指定して要素を全て得る
- 属性を指定して要素を得る
- 再帰をやめる
- テキストを指定して要素を得る
- 再帰上限数を指定する
- タグ名とクラス名を指定して要素を得る
- タグ名とID名を指定して要素を得る
- クラス名を正規表現で指定して検索し要素を得る
find_allの構造
find_all()
の構造について解説します。
公式ドキュメントは↓です。
find_all()
は↓のような構造になっています。
find_all(
self,
name=None, # タグ名を表す文字列
attrs={}, # 属性を表す辞書
recursive=True, # 再帰をするならTrue
text=None, # 要素内のテキストを表す文字列
limit=None, # 再帰上限数
**kwargs # キーワード引数
)
↓から引数と返り値の解説です。
name(第1引数)
タグ名を表す文字列またはリスト、タプルを指定します。
または整数やbool
も指定できます。
find_all()
で特定のタグを取得したい、たとえばdiv
のタグを取得したい場合は、この引数name
に文字列でdiv
を指定します。
soup.find_all('div')
リストまたはタプルを指定した場合は、その要素の文字列がタグ名として参照されます。
複数のタグを取得したい場合はこの引数name
にリストを指定するといいでしょう。
soup.find_all(['p', 'span'])
True
を指定した場合、find_all()
は全ての要素を取得するようになります。
class
名やid
名で検索するタグを絞りたい時に使われます。
soup.find_all(True)
attrs(第2引数)
タグの属性値を表す辞書を指定します。
たとえば値bird
を持つ属性class
を表現したい場合は
soup.find_all('p', attrs={ 'class': 'bird' })
↑のような指定になります。
辞書には複数の属性を指定することも可能です。
{
'class': 'bird',
'id': 'dog',
}
複数の属性を指定した場合は検索はAND
検索になります。
# classとidでAND検索
soup.find_all('p', attrs={'class': 'bird', 'id': 'dog'})
辞書のキーに対する値にはリストまたはタプルも指定することが出来ます。
リスト内に複数の文字列を指定した場合はOR
検索になります。
# classをOR検索
soup.find_all('p', attrs={ 'class': ['a', 'b'] })
attrs
のキーに対する値にはコンパイル済みの正規表現オブジェクトも指定できます。
検索に正規表現を使いたい場合は↓のようにします。
import re
soup.find_all('p', attrs={ 'class': re.compile(r'Dr.*') })
recursive(第3引数)
再帰的に検索するならTrue
, 非再帰的に検索するならFalse
を指定します。
デフォルトではTrue
です。つまりfind_all()
はデフォルトでは再帰的に検索します。
再帰と言うのは、ツリー構造を再帰的、つまり、親の要素から子の要素を辿るという意味になります。
再帰的検索をFalse
にすると、親の要素から子の要素へ辿らなくなります。
text(第4引数)
タグの持つテキストまたはリストやタプル、正規表現オブジェクトを指定します。
タグの内容を表す文字列です。
たとえば↓のようなタグがあったとして、
<p>Happy!</p>
↑のタグを取得したい場合はfind_all()
の引数text
に文字列でHappy!
と指定します。
soup.find_all('p', text='Happy!')
複数のテキストを指定してOR
検索したい場合はtext
にリストやタプルを指定します。
soup.find_all('p', text=['Bob', 'Alice'])
また、コンパイルされた正規表現オブジェクトを渡し、正規表現で指定することも可能です。
import re
soup.find_all('p', text=re.compile(r'The.*$'))
limit(第5引数)
検索における再帰上限数を正数で指定します。
デフォルトではNone
です。
「再帰上限」とは「どれぐらいの深さまで検索するか」という意味になります。
たとえば↓のようなHTMLの場合、ツリー全体の深さは2
です。
<div>
<p>I'm here!</p>
</div>
このツリーを深さ1
まで、つまりp
タグまで辿らないようにしたい場合は、このlimit
引数に1
を指定します。
soup.find_all('div', limit=1)
kwargs(第6引数)
オプションの引数です。
よく使われるのはclass_
やid
です。
これらの引数はattrs
のショートカットとして使われます。
soup.find_all('p', class_='bird')
soup.find_all('p', id='pig')
find_all()の返り値
find_all()
の返り値はbs4.element.ResultSet
です。
ResultSet
はlist
を継承したfor
文で回せるオブジェクトです。
from bs4 import BeautifulSoup
html = '''
<p>1</p>
<p>2</p>
<p>3</p>
'''
soup = BeautifulSoup(html, 'html.parser')
resultset = soup.find_all('p')
print(type(resultset))
↑のコードの実行結果↓。
<class 'bs4.element.ResultSet'>
BeautifulSoup4にけるResultSet
の実装は非常にシンプルです。
実装は↓のようになっています。
class ResultSet(list):
"""A ResultSet is just a list that keeps track of the SoupStrainer
that created it."""
def __init__(self, source, result=()):
super(ResultSet, self).__init__(result)
self.source = source
タグ名を指定して要素を全て得る
find_all()
の引数name
にタグ名を指定すると、そのタグを検索して取得することが可能です。
↓はli
タグを取得している所です。
from bs4 import BeautifulSoup
html = '''
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
'''
soup = BeautifulSoup(html, 'html.parser')
for el in soup.find_all('li'): # <- タグ名に li を指定
# ResultSetをfor文で回し、要素のtext属性を参照
print(el.text)
1
2
3
find_all()
はデフォルトで再帰的に検索します。
ですのでli
が入れ子になっていたらその入れ子になっているタグも取得されます。
例えば↓のようなHTMLです
<ul>
<li>1</li>
<li>2</li>
<li>
<ul>
<li>3</li>
<li>4</li>
</ul>
</li>
</ul>
複数のタグ名を指定して要素を全て得る
find_all()
の引数name
にリストを指定し、リスト内の要素にタグ名を書くと、それらのタグ名の要素を取得することが出来ます。
↓はp
タグとpre
タグを取得している所です。
from bs4 import BeautifulSoup
html = '''
<p>1</p>
<pre>2</pre>
<span>3</span>
'''
soup = BeautifulSoup(html, 'html.parser')
# タグ名pとpreを持つ要素を検索する
for el in soup.find_all(['p', 'pre']):
# ResultSetをfor文で回し、要素のtext属性を参照
print(el.text)
↑の出力結果↓。
1
2
リストはタプルでも大丈夫です。
soup.find_all(('p', 'pre'))
属性を指定して要素を得る
タグの属性を指定して検索したい場合はfind_all()
の引数attrs
に辞書を指定します。
↓はclass
属性を指定して検索しています。
from bs4 import BeautifulSoup
html = '''
<ul>
<li class="bird">1</li>
<li class="dog">2</li>
<li class="bird">3</li>
</ul>
'''
soup = BeautifulSoup(html, 'html.parser')
# class属性にbirdを持つliタグを検索
for el in soup.find_all('li', attrs={'class': 'bird'}):
# ResultSetをfor文で回し、要素のtext属性を参照
print(el.text)
↑の出力結果↓。
1
3
ちなみにattrs
はAttributes(属性群)
の略です。
再帰をやめる
find_all()
はデフォルトでは↓のようにコンテンツを再帰的に検索します。
from bs4 import BeautifulSoup
html = '''
<span id="1">
<span id="2">
<span id="3"></span>
</span>
</span>
'''
soup = BeautifulSoup(html, 'html.parser')
for el in soup.find_all('span'):
# ResultSetをfor文で回し、要素のid属性を参照
print(el.get('id'))
↑の出力結果↓。
1
2
3
この再帰的検索をやめて、表面のレイヤーだけの検索をしたい場合はfind_all()
のrecursive
引数にFalse
を指定します。
for el in soup.find_all('span', recursive=False):
print(el.get('id'))
↑の出力結果↓。
1
テキストを指定して要素を得る
タグの持つテキストを指定して検索したい場合はfind_all()
の引数text
にテキストを表す文字列を指定します。
from bs4 import BeautifulSoup
html = '''
<p id="a">1</p>
<p>2</p>
<div>
<p id="b">1</p>
<p>2</p>
</div>
'''
soup = BeautifulSoup(html, 'html.parser')
# textに「1」を持つタグpを検索する
for el in soup.find_all('p', text='1'):
# ResultSetをfor文で回し、要素のid属性を参照
print(el.get('id'))
↑の出力結果↓。
a
b
再帰上限数を指定する
再帰の上限数を指定すると、特定の階層まで検索して、以降は検索しないという動作を得ることができます。
この再帰上限数の指定はfind_all()
の引数limit
に正数で指定します。
from bs4 import BeautifulSoup
html = '''
<div id="1">
<div id="2">
<div id="3"></div>
</div>
</div>
'''
soup = BeautifulSoup(html, 'html.parser')
# 再帰は2まで
for el in soup.find_all('div', limit=2):
# ResultSetをfor文で回し、要素のid属性を参照
print(el.get('id'))
↑の出力結果↓。
1
2
タグ名とクラス名を指定して要素を得る
クラス名の指定にはfind_all()
の引数attrs
にクラス名を指定すればいけますが、ショートカットとしてclass_
引数に指定することも出来ます。
from bs4 import BeautifulSoup
html = '''
<p class="bird">1</p>
<p class="dog">2</p>
<p class="bird">3</p>
'''
soup = BeautifulSoup(html, 'html.parser')
# クラス名がbirdの要素を得る
for el in soup.find_all('p', class_='bird'):
# ResultSetをfor文で回し、要素のtext属性を参照
print(el.text)
↑の出力結果↓。
1
3
タグ名とID名を指定して要素を得る
ID名もクラス名と同様にショートカットが使えます。
find_all()
の引数id
にID名を指定すれば、そのID属性を持つタグを検索します。
from bs4 import BeautifulSoup
html = '''
<p id="bird">1</p>
<p id="dog">2</p>
<p id="bird">3</p>
'''
soup = BeautifulSoup(html, 'html.parser')
# クラス名がbirdの要素を得る
for el in soup.find_all('p', id='bird'):
# ResultSetをfor文で回し、要素のtext属性を参照
print(el.text)
↑の出力結果↓。
1
3
クラス名を正規表現で指定して検索し要素を得る
すべてのタグを対象に検索します。
クラス名がblue-.*
という正規表現にマッチしたらその要素を取得します。
from bs4 import BeautifulSoup
import re
html = '''
<p class="blue-mountain" id="1"></p>
<div class="red-sea" id="2">
<p class="red-mountain" id="3"></p>
<span class="blue-ocean" id="4"></span>
</div>
'''
soup = BeautifulSoup(html, 'html.parser')
for el in soup.find_all(True, class_=re.compile(r'blue-.*')):
# ResultSetをfor文で回し、要素のtext属性を参照
print(el.get('id'))
↑の出力結果↓。
1
4
おわりに
BeautifulSoup4を使ったパースは最近のPythonでは非常に一般的になっています。
その中でもfind_all()
はとても人気があるメソッドです。
find_all()
をマスターすることはBeautifulSoup4のマスターにも繋がるかと思います。
この記事で要点を押さえてマスターを目指しましょう。
🦝 < フォースの力を信じよ
参考
- Beautiful Soup Documentation — Beautiful Soup 4.9.0 documentation
- BeautifulSoup4のソースコード