BeautifulSoupを用いたHTMLデータの検索方法


1.概要

データ解析用にいろいろなタグ情報をHTMLに埋め込み、「その埋め込んだデータが正しく表示されているか」を自動テストで検出できないか、いろいろ方法を調べてみました。

その中で、PythonのBeautifulSoupを用いて対象のHTMLデータを検索出来たので、その方法をまとめます。
※本件は検索方法のみで解析については記載しておりません。

2.検索方法の考え方

BeautifulSoupで対象のHTMLデータを取得するには、まず起点となる<>で囲まれたデータを見つけます。そして、起点となるタグに含まれている情報を1つ1つ記載していくことで、HTMLデータを検索します。

起点とすべきデータはユニークな値を持つものを指定すると、その項目のみを指定すればいいので、検索プログラムの記載がシンプルになります。

また、3章以降の例文を動作するには、以下のプログラムが記載されていることを前提としております。


import requests
import re
from bs4 import BeautifulSoup

res = requests.get('ここに解析対象のURLを記載')
c=res.content
soup = BeautifulSoup(c,'html.parser')

#ここに例文を記載

print(elems)

3.基本的なデータの取得方法

取得したいデータが1つの場合はfindを、複数の場合はfind_allを利用します(その他にselectという方法もありますが、今回除外します)。以下の例文ではfind_allを用いています。

・直接タグを指定するパターン

#検索したいタグの構造
<script></script>
elems = soup.find_all("script")

単純なタグで囲まれた部分を検索する場合、利用します。

・複数のタグを指定するパターン(リストを利用)

#検索したいタグの構造
<h1></h1>
<div></div>
elems = soup.find_all(["h1","div"])

タグで囲まれているデータを複数検索したい場合、利用します。

・キーワードを指定するパターン(完全一致)

#検索したいタグの構造
<a class = "test"></a>
elems = soup.find_all(class_="test")

各タグの中で「=」を用いて値が代入されている項目がある場合、利用します。
また、classを指定する場合は「class_」とする必要があります。(Pythonの予約語にclassが使われているので)
検索項目が2つある場合は、以下のように[]を使います。

elems = soup.find_all(id=["test1", "test2"])

・キーワードを指定するパターン(部分一致)

#検索したいタグの構造
<a href="http://○○/△△.html"><a>
elems = soup.find_all(href=re.compile("http://"))

「=」で代入されている値を部分的に検索する場合などに利用します。

・キーワードを指定するパターン(attrs属性を利用した完全一致)

#検索したいタグの構造
<a href="http://○○/△△.html"><a>
elems = soup.find_all(attrs={"href":"http://○○/△△.html"})

HTML5のdataタグなど、キーワードとして使えないものがあるときは「attrs」を利用します

#例)
× elems = soup.find_all("meta",name="test")
TypeError: find_all() got multiple values for argument 'name'

○ elems = soup.find_all("meta",attrs={"name":"test"})

・項目のありなしを確認するパターン(値が入っていればTrue、入っていなければFalse)

#検索したいタグの構造
<a href="http://○○/△△.html"><a>
elems = soup.find_all(href=True)

hrefなどのタグに何でもいいので値が入っているものを調べる場合、利用します。
値が入っていない場合は、以下のようにFalseを指定します

elems = soup.find_all(id=False)

・タグで囲まれているテキストを検索するパターン(完全一致)

#検索したいタグの構造
<a href="http://○○/△△.html">ご利用案内<a> #ここのご利用案内のみ検索したい
elems = soup.find_all(text='ご利用案内')

タグで囲まれた中のテキストのみ抽出したい場合、利用します。

・タグで囲まれているテキストを検索するパターン(部分一致)

#検索したいタグの構造
<a href="http://○○/△△.html">ご利用案内<a> #ここのご利用案内のみ検索したい
elems = soup.find_all(text=re.compile("ご"))

タグで囲まれた中のテキストのみ抽出したい場合、利用します。こちらは完全一致ではなく、部分一致を用いています。

・収集するタグの数を指定するパターン(find_allのみ利用できる)

#検索したいタグの構造
<p>test1</p> #ここだけ取得したい
<p>test2</p>
elems = soup.find_all('p', limit=1)

複数のタグから指定した数だけ取得したい場合、利用します。

4.応用編

指定した構造データを見つけるには、上記の基本パターンを組み合わせて使うことが多いです。

・タグとキーワードを指定

#検索したいタグの構造
<meta name="test">
elems = soup.find_all("meta",attrs={"name":"test"})

metaというタグにname=testをしているタグを検索する場合、利用します。

・タグとテキストを指定

#検索したいタグの構造
<a href="http://○○/△△.html">テストです<a>
elems = soup.find_all("a",text="テストです")

aタグで「テストです」とテキスト文が記載されているタグを検索する場合

・attrsで項目を複数指定

#検索したいタグの構造
<a href="http://○○/△△.html" title="test">テストです</a>
elems = soup.find_all(attrs={"title":"test","href":"http://○○/△△.html"})

・起点の前後のタグを指定

また、同じようなタグが複数あるため指定の構造データを検索できない場合は、起点となる構造データを決めてそのデータの前後を検索するようにします。前後の検索には、「next_element」と「previous_element」を利用します。
(next_elementは後の要素を検索する場合、previous_elementは前の要素を検索する場合に利用します)

#検索したいタグの構造
<ui>
 <li>
  <a href="http://○○/△△.html">test</a>
 </li>
</ui>

#取得したいのはここ
<ui>
 <li>
  <a href="http://□□/☆☆.html">test2</a> #ここを起点にデータを取得する
 </li>
</ui>
elems = soup.find_all("a",href="http://□□/☆☆.html")
elems2 = elems[0].previous_element.previous_element #.previous_elementを2回使い、<ui><li>までを含むようにする

・関数を作成して指定

関数を作成し、複雑なタグ構造からデータを取得することもできます。

#検索したいタグの構造
<a class="test"></a> #ここだけ取得したい
<a id="test"></a>
def has_class_but_no_id(tag):
    return tag.has_attr('class') and not tag.has_attr('id')

elems = soup.find_all(has_class_but_no_id)