【问题标题】:BeautifulSoup webscraping find_all( ): finding exact matchBeautifulSoup webscraping find_all():找到完全匹配
【发布时间】:2014-05-08 17:49:54
【问题描述】:

我正在使用 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>

使用 BeautifulSoup,我只想找到属性为 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>]

【问题讨论】:

    标签: python html regex web-scraping beautifulsoup


    【解决方案1】:

    您可以解决此问题,并通过强制精确匹配仅捕获 Product 1Product 2gazpacho

    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']
    

    【讨论】:

      【解决方案2】:

      更改您的代码

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

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

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

      【讨论】:

        【解决方案3】:
        soup.findAll(attrs={'class': re.compile(r"^product$")})
        

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

        【讨论】:

        • 对不起,我看不出问题中的版本有什么不同,你能告诉我它是什么吗?
        【解决方案4】:

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

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

        css-selectors

        【讨论】:

        • 感谢您的回复,但我正在尝试查找“产品” div,而不是“产品特殊” div .... 使用 soup.select('div.product.special') 会返回“特殊”产品..
        • 糟糕,误读了您的问题。那么另一种方法是删除匹配“.product.special”的div,然后您可以安全地搜索“.product”而不会遇到其他人。
        • 你不能在:not伪选择器上使用这种方法吗:div.product:not(.special)
        【解决方案5】:

        在 BeautifulSoup 4 中,class 属性(以及其他几个属性,例如accesskey 和表格单元格元素上的headers 属性)被视为一个集合;您匹配属性中列出的各个元素。这遵循 HTML 标准。

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

        您必须在此处使用 custom function 来匹配该类:

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

        我使用lambda 创建了一个匿名函数;每个标签在名称上匹配(必须是'div'),并且类属性必须完全等于列表['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"],
            }
        

        【讨论】:

        • 终于找到了一个可行的解决方案!!我有两个要匹配的类,并且正在使用soup.find_all('div', {'class': ['class1','class2']}),但它也使用了只有class2divs。有了它,它正在做我所期望的。不知道为什么我使用的那个不起作用...
        猜你喜欢
        • 2021-06-29
        • 1970-01-01
        • 2018-03-28
        • 2015-02-05
        • 1970-01-01
        • 2013-05-21
        • 1970-01-01
        • 2021-01-06
        • 1970-01-01
        相关资源
        最近更新 更多