find_allでタグがヒットしない時の検索方法【BeautifulSoup】

BeautifulSoupの find() と find_all() では、タグのテキストでタグを検索することができます。

属性からうまくタグを特定できないときに、よく使います。

ですが、text=""string=""の引数にテキストを渡しても、タグがヒットしないケースが多々あるんですね。

そういった、BeautifulSoupでタグが取得できないときの対策コードの紹介です。

 

テキストは確かに含まれているのに、正規表現を使ってもヒットしない。

find(text="検索テキスト")find(string="検索テキスト")が None になる。

find_all(text="検索テキスト")find_all(string="検索テキスト")が空のリストになる。

その対策を紹介します。

 

対策といっても、『find()』や『find_all()』に検索用の関数を作って渡すだけです。

とても簡単です。

タグが取得できない原因は、『find()』や『find_all()』が見ている『.string(ドットストリング)』が『None』になるためです。

対策としては、『.string』以外の変数で検索するような関数を作って、『find()』や『find_all()』に渡すアプローチになります。

ここでは、『.text(ドットテキスト)』で検索するコード例を紹介します。

 

以下、『タグがヒットしない原因の詳細』と『ヒットさせるための検索コード例』です。

なぜタグがヒットしないのか?

原因は、タグのテキストが<br>タグや<span>タグなどを含んでいるためです。

なぜ、テキストがタグを含んでいると検索できないのか?

そういった状態のテキストでは、タグの『.string』が『None』になってしまうんですね。

この『.string』という変数は、『find()』と『find_all()』がテキスト検索のときに見ている変数です。

BeautifulSoupの公式マニュアルに、『find()』と『find_all()』が『.string』を見て検索している旨(むね)が書かれていました。

(参考)Beautiful Soup Documentation – find_all() – The string argument

つまり、『textやstringに渡したテキスト』と『None』を比較する形になっていたので、ヒットしなかったと考えられます。

では、どうしたらヒットするのか?

タグをヒットさせる方法

『.string』以外の変数でタグを検索するようにします。

たとえば、個々のタグの『.text(ドットテキスト)』変数が使えます。

『.text』なら、テキストがさらにタグを含んでいるケースでも、テキストだけを抽出した結果が入っています。

実際に『.text』を参照するような検索用の関数を作って、『find()』や『find_all()』に渡したところ、うまくヒットさせることができました。

検索コード例

テキストで検索できないタグを、検索できるようにします。

サンプルHTML

テキストに<br>タグを含んでいる<a>タグの例です。

<html>
<body>
<a href="https://****">アンカーテキスト</a>
<a href="https://****">アンカー<br>
テキスト</a>
</body>
</html>

2個目の<a>タグは、『find()』や『find_all()』に検索テキストを渡してもヒットしません。

代わりに、以下の検索用関数を渡すとヒットします。

検索用の関数

『find()』や『find_all()』に渡す検索用の関数です。

ヒットさせたい条件の時に True を返すようにすればOKです。

def find_text(x):
    """検索関数"""
    if x.name == 'a':
        if x.text == 'アンカー\nテキスト':
            return True
    return False

さて、引数の『x』に何が来るか?です。

これは、関数を『find_all()』の『どの引数に渡したか?』によって変わります。

 

find_all(name=find_text) のときは、『x』に個々のタグのインスタンスが入っています。

『name』は、普段タグ名を渡している第1引数ですね。

'a'とか'div'とか'table'などのタグ名を渡して、検索に使っているところです。

『x』にタグのインスタンスが来るので、x.nameでタグ名が取得できますし、x.textでテキストの抽出結果が取得できます。

これらを検索に利用します。

 

find_all(text=find_text) または find_all(string=find_text) のときは、『x』に個々のタグの『.string』変数が入っています。

つまり、『単なる文字列型』か『None』になります。

コード全体

サンプルHTMLをBeautifulSoupに渡して、『find_all()』で検索するコード例です。

2個目の<a>タグにヒットするような検索関数『find_text()』を作って、『find_all()』に渡しています。

"""find_all()に検索関数を渡して検索するコード例"""
from bs4 import BeautifulSoup

html = """<html>
<body>
<a href="https://****">アンカーテキスト</a>
<a href="https://****">アンカー<br>
テキスト</a>
</body>
</html>"""

soup = BeautifulSoup(html, 'lxml')

def find_text(x):
    """検索関数"""
    if x.name == 'a':
        if x.text == 'アンカー\nテキスト':
            return True
    return False

for tag in soup.find_all(name=find_text, recursive=True):
    print('string: %s' % tag.string)
    print('text: %s' % tag.text)

リカーシブ(recursive)は、配下のタグまで再帰的に検索するための引数です。

実行結果は以下の通りです。

string: None
text: アンカー
テキスト

きちんと2個目の<a>タグがヒットして、その『.string』と『.text』が print() で表示されています。

このような感じで、BeautifulSoupのテキスト検索でタグが検索できないときは、『.text』で検索する関数を渡すと、うまくいく場合があります。

タイトルとURLをコピーしました