美丽汤网页抓取find_all():找到完全匹配



我正在使用Python和BeautifulSoup进行网页抓取。

假设我有以下 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>

使用美丽汤,我只想找到属性为class="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 标准。

因此,不能将搜索限制为仅一个类。

您必须在此处使用自定义函数来与类进行匹配:

result = soup.find_all(lambda tag: tag.name == 'div' and 
                                   tag.get('class') == ['product'])

我使用了一个lambda来创建一个匿名函数;每个标签在名称上匹配(必须'div'),并且class属性必须与列表['product']完全相等;例如,只有一个值。

演示:

>>> 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"],
    }

你可以像这样使用CSS选择器:

result = soup.select('div.product.special')

CSS 选择器

soup.findAll(attrs={'class': re.compile(r"^product$")})

此代码匹配在其类末尾没有product的任何内容。

您可以通过强制执行精确匹配来解决此问题,并使用西班牙凉菜汤仅捕获产品 1产品 2

from gazpacho import Soup
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>
"""
soup = Soup(html)
divs = soup.find("div", {"class": "product"}, partial=False)
[div.text for div in divs]

输出准确:

['Product 1', 'Product 2']

将代码从

result = soup.findAll(attrs={'class': re.compile(r"^product$")})

result = soup.find_all(attrs={'class': 'product'})

结果是一个列表并通过索引访问

最新更新