私はPythonとBeautifulSoupをWebスクレイピングに使用しています。
次のHTMLコードを取得する必要があるとしましょう。
<body>
<div class="product">Product 1</div>
<div class="product">Product 2</div>
<div class="product special">Product 3</div>
<div class="product special">Product 4</div>
</body>
BeautifulSoupを使用して、「特別な」製品ではなく、class = "product"属性(Product 1および2のみ)を持つ製品のみを検索したい
次のことを行う場合:
result = soup.find_all('div', {'class': 'product'})
結果には、すべての製品(1、2、3、および4)が含まれます。
クラスが「製品」と完全に一致する製品を見つけるにはどうすればよいですか?
私が実行したコード:
from bs4 import BeautifulSoup
import re
text = """
<body>
<div class="product">Product 1</div>
<div class="product">Product 2</div>
<div class="product special">Product 3</div>
<div class="product special">Product 4</div>
</body>"""
soup = BeautifulSoup(text)
result = soup.findAll(attrs={'class': re.compile(r"^product$")})
print result
出力:
[<div class="product">Product 1</div>, <div class="product">Product 2</div>, <div class="product special">Product 3</div>, <div class="product special">Product 4</div>]
BeautifulSoup 4では、class
属性(およびテーブルセル要素のaccesskey
やheaders
属性などの他の属性)はセットとして扱われます。属性にリストされている個々の要素と照合します。これはHTML標準に従います。
そのため、検索を1つのクラスだけに制限することはできません。
代わりに カスタム関数 を使用して、クラスと照合する必要があります。
result = soup.find_all(lambda tag: tag.name == 'div' and
tag.get('class') == ['product'])
lambda
を使用して匿名関数を作成しました。各タグは名前で一致し('div'
である必要があります)、クラス属性はリスト['product']
と正確に等しくなければなりません。例えば値は1つだけです。
デモ:
>>> from bs4 import BeautifulSoup
>>> text = """
... <body>
... <div class="product">Product 1</div>
... <div class="product">Product 2</div>
... <div class="product special">Product 3</div>
... <div class="product special">Product 4</div>
... </body>"""
>>> soup = BeautifulSoup(text)
>>> soup.find_all(lambda tag: tag.name == 'div' and tag.get('class') == ['product'])
[<div class="product">Product 1</div>, <div class="product">Product 2</div>]
完全を期すために、BeautifulSoupソースコードからのそのようなすべてのセット属性を以下に示します。
# The HTML standard defines these attributes as containing a
# space-separated list of values, not a single value. That is,
# class="foo bar" means that the 'class' attribute has two values,
# 'foo' and 'bar', not the single value 'foo bar'. When we
# encounter one of these attributes, we will parse its value into
# a list of values if possible. Upon output, the list will be
# converted back into a string.
cdata_list_attributes = {
"*" : ['class', 'accesskey', 'dropzone'],
"a" : ['rel', 'rev'],
"link" : ['rel', 'rev'],
"td" : ["headers"],
"th" : ["headers"],
"td" : ["headers"],
"form" : ["accept-charset"],
"object" : ["archive"],
# These are HTML5 specific, as are *.accesskey and *.dropzone above.
"area" : ["rel"],
"icon" : ["sizes"],
"iframe" : ["sandbox"],
"output" : ["for"],
}
soup.findAll(attrs={'class': re.compile(r"^product$")})
このコードは、クラスの最後にproduct
がないものに一致します。