【问题标题】:BeautifulSoup: find Class names: AND + NOTBeautifulSoup:查找类名:AND + NOT
【发布时间】:2018-07-05 11:09:23
【问题描述】:

我在 HTML 中有两组不同的 div 标签:

<div class="ABC BCD CDE123">

<div class="ABC BCD CDE234">

<div class="ABC BCD CDE345">

<div class="ABC XYZ BCD">

我想选择所有带有 ABC 和 BCD 的标签,但不包含带有 BeautifullSoup4 的 XYZ 类。

我已经知道这种方法:

soup.find_all('div', class_=['ABC','BCD'])

搜索为OR (因此必须存在 ABC 或 BCD)。

我在这里也知道这种方法:

def myfunction(theclass):
    return theclass is not None and len(theclass)=5
soup.find_all('div', class_=myfunction)

这将返回所有类名长度为 5 的 div

然后我尝试用这个来解决我的问题:

soup.find_all('div', class_ = lambda x: x and 'ABC' and 'BCD' in x.split() and x and 'XYZ' not in x.split())

但这不起作用。 所以我尝试用这种方法调试它:

def myfunction(theclass):
    print theclass
    return True
soup.find_all('div', class_=myfunction)

问题似乎是,来自这样的标签:

<div class="ABC BCD CDE123">

只有'ABC'被交给myfunction,所以theclass = 'ABC' 而不是我所期望的theclass ='ABC BCD CDE123'。 这也是我猜测 lambda 函数失败的原因。

任何线索我可以根据我的要求过滤标签:

我想选择所有带有 ABC 和 BCD 的标签,但不包含带有 BeautifullSoup4 的 XYZ 类。

【问题讨论】:

  • 我当然可以用一些丑陋的多线黑客自己解决这个问题,但我在这里寻找一个好的和干净的解决方案。
  • Lxml + cssselect 可以让你这样做:.ABC.BCD:not(.XYZ) - 不过我不确定 BS4。

标签: python html beautifulsoup


【解决方案1】:

我不知道一步式解决方案,但您可以使用 CSS 选择,然后过滤掉您不想要的元素。

from bs4 import BeautifulSoup

html = '''
<div class="ABC BCD CDE123"></div>
<div class="ABC BCD CDE234"></div>
<div class="ABC BCD CDE345"></div>
<div class="ABC XYZ BCD"></div>
<div class="ABC XYZ AAC"></div>
<div class="ABC AAC"></div>
'''

soup = BeautifulSoup(html, "html.parser")
divs = soup.select('div.ABC.BCD')
result = [div for div in divs if "XYZ" not in div['class']]

【讨论】:

    【解决方案2】:

    你的方法是正确的,但你错过了一件事。 BeautifulSoup 在列表中转换属性class 的值。

    例如:

    >>> soup.div['class']
    ['ABC', 'BCD', 'CDE123']
    

    不使用x.split(),直接检查值是否在列表中。

    代码:

    html = '''
    <div class="ABC BCD CDE123"></div>
    <div class="ABC BCD CDE234"></div>
    <div class="ABC BCD CDE345"></div>
    <div class="ABC XYZ BCD"></div>'''
    
    soup = BeautifulSoup(html, 'html.parser')
    print(soup.find_all('div', class_=lambda c: 'ABC' in c and 'BCD' in c and 'XYZ' not in c))

    输出:

    [<div class="ABC BCD CDE123"></div>,
     <div class="ABC BCD CDE234"></div>,
     <div class="ABC BCD CDE345"></div>]
    

    【讨论】:

      【解决方案3】:

      这可以使用 SET 来完成。 获取所有类 ABC 和 BCD 的结果列表。将结果包含在 python SET 中。对 XYZ 应用同样的方法。您现在将有两个 SET,一个用于 ABC 和 BCD,另一个用于 XYZ。减去两个集合

      要在搜索列表中使用 ABC 和 BCD,请使用 select 函数而不是 find_all

      from bs4 import BeautifulSoup
      
      data = '''
      <div class="ABC BCD CDE123"></div>
      <div class="ABC BCD CDE234"></div>
      <div class="ABC BCD CDE345"></div>
      <div class="ABC XYZ BCD"></div>
      <div class="ABC XYZ AAC"></div>
      <div class="ABC AAC"></div>
      '''
      
      soup = BeautifulSoup(data)
      ABC_BCD = set(soup.select('div.ABC.BCD'))
      XYZ     = set(soup.select('div.XYZ'))
      result = ABC_BCD - XYZ
      for element in result:
          print element
      

      输出

      <div class="ABC BCD CDE234"></div>
      <div class="ABC BCD CDE123"></div>
      <div class="ABC BCD CDE345"></div>
      

      使用 find_all 使用相同的代码

      ABC_BCD = set(soup.find_all('div', class_=['ABC','BCD']))
      XYZ     = set(soup.find_all('div', class_=['XYZ']))
      result = ABC-BCD
      for element in result:
          print element
      

      输出是

      <div class="ABC BCD CDE234"></div>
      <div class="ABC AAC"></div> #This is what we dont need
      <div class="ABC BCD CDE123"></div>
      <div class="ABC BCD CDE345"></div>
      

      【讨论】:

      • 谢谢你,你的第一个代码段有一个小错误:它应该读作“for element in result: print element”来给出你在下一个代码段中声明的结果。关于使用 find_all 的方法,使用“soup.find_all('div', class_=['ABC','BCD'])”选择是使用 OR 关系进行选择,这不是我们想要的。 (顺便说一句,那里也应该有“result = ABC_BCD - XYZ”)但我喜欢将结果放入 SET 然后减去的方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-08-02
      • 1970-01-01
      • 1970-01-01
      • 2021-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多