繁体   English   中英

BeautifulSoup:根据自身属性和祖先属性查找元素

[英]BeautifulSoup: Find an element based it's own attributes and those of an ancestor

我正在使用BeautifulSoup来解析 HTML 并在页面上定位特定元素。

有没有一种方法可以合理化以下尝试,即根据目标元素的属性及其祖先的属性,通过单个find()调用提取单个元素?

HTML

<ul class="info">
  <li>Name: Mickey Mouse</li>
  <li>Height: 3ft</li>
</ul>
<ul class="info">
  <li>Rating: 5</li>
  <li>Score: 6</li>
</ul>
<ul class="info">
  <li>Age: 20</li>
  <li>Appearances: 100</li>
</ul>

PYTHON

ancestors = soup.find_all("ul", class_="info")

for ancestor in ancestors:
    elem = ancestor.find("li", string=lambda s: s.startswith("Rating: "))
    if elem: break

换句话说,我可以在find()调用中添加祖先元素的搜索条件吗?

这个问题是关于find()方法的能力的一般问题,而不是关于上面给出的任意特定示例。

使用select_one()方法可以考虑祖先属性——它使用 CSS 选择器。

例如(忽略前缀文本对 select 的需求):

soup.select_one("ul.info li")

这将返回所有<li>标签,这些标签的祖先是<ul>标签,其值为info的值为 class。

阅读文档,我看不到使用 Beautiful Soup "pure" API 的等效单线可以做同样的事情。

您可以使用find这样做。 我不太确定“纯”API 所暗示的是什么,但让我们开始吧。

所以,首先,让我们从find开始。 Find 有很多功能。 您可以按标签名称、属性属性、标签名称或属性属性上的正则表达式,甚至是内容来过滤元素。 您还可以将函数传递给 find,这是做更高级事情的唯一方法。

from bs4 import BeautifulSoup

HTML = """
<ul class="info">
  <li>Name: Mickey Mouse</li>
  <li>Height: 3ft</li>
</ul>
<ul class="info">
  <li>Rating: 5</li>
  <li>Score: 6</li>
</ul>
<ul class="info">
  <li>Age: 20</li>
  <li>Appearances: 100</li>
</ul>
"""

def get_ratings(el):
    if el.name == 'li' and el.string.startswith("Rating: "):
        parent = el.parent
        if parent.name == 'ul' and 'info' in parent.attrs['class']:
            return True
    return False


soup = BeautifulSoup(HTML, 'html.parser')

print(soup.find(get_ratings))

也就是说,您也可以使用 CSS 选择器来执行此操作。 我们不一定要测试“Ratings:”的前缀,但我们可以使用名为:-soup-contains()的自定义 CSS 选择器测试元素是否包含“Ratings:”:

from bs4 import BeautifulSoup

HTML = """
<ul class="info">
  <li>Name: Mickey Mouse</li>
  <li>Height: 3ft</li>
</ul>
<ul class="info">
  <li>Rating: 5</li>
  <li>Score: 6</li>
</ul>
<ul class="info">
  <li>Age: 20</li>
  <li>Appearances: 100</li>
</ul>
"""

soup = BeautifulSoup(HTML, 'html.parser')

print(soup.select_one('ul.info li:-soup-contains("Rating: ")'))

两者都会产生:

<li>Rating: 5</li>

您是否考虑在get_rating function 中预先编写您的特殊逻辑,然后在一行中使用它就足够了? 如果没有,那么答案是没有办法,至少那是漂亮的。 您绝对可以构造一个单行代码来测试该元素及其父元素,但它会是一个长而丑陋的单行代码,从而违背了单行代码的目的。 但是您可以将所需的逻辑封装在 function 中,并将其提供给findfind_all以使其用法非常简单。

此外,您可以使用selectselect_one执行此操作,而无需其他功能。 这是你的选择。

我仍然不确定“纯”API 是什么意思,但从技术上讲,这两个都是纯 API,一个只需要您编写自己的 function 并将其传入。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM