python 如何基于可能跨越多个子标签的字符串找到元素?

wi3ka0sx  于 2024-01-05  发布在  Python
关注(0)|答案(3)|浏览(133)

我试图根据已知的文本字符串来识别文档中的特定元素。

  1. soup.find(string=re.compile(".*some text string.*"))

字符串
然而,已知字符串中可能有(多个)子元素。例如,如果这是我们的文档:

  1. test_doc = BeautifulSoup("""<html><h1>Title</h1><p>Some <b>text</b></p>""")


我正在寻找一个特定的元素。关于这个元素,我唯一知道的是它包含文本“Some text”。我 * 不 * 知道其中的单词“text”在一个子粗体标记中。

  1. test_doc.find(string=re.compile(".*Some text.*"))


None,因为“text”在子标记中。
如果我不知道文本是否/如何分解成子标签,我如何返回父标签(在我的示例中是p标签)和所有子标签?

kupeojn6

kupeojn61#

我的第一个想法是,在不清楚哪些或多少个标签可以嵌套的背景下,这里是css selector和伪类:-soup-contains("some text"),但这可能超过了标记,因为它还返回所有包含文本的重叠组合。
当然,这不是最好的甚至是最有弹性的方法,但也许可以从中找到一个解决方案,那就是在每种情况下挑选出容纳文本的最小元素组合:

  1. from bs4 import BeautifulSoup
  2. test_doc = BeautifulSoup("""<html><h1>Title</h1><p>Some <b>text</b></p><div><p>Some <i>text</i> different than <div>before</div></p></div>""", 'html.parser')
  3. selection = test_doc.select(':-soup-contains("Some text")')
  4. for i,el in enumerate(selection):
  5. if len(selection[i].find_all()) <len(selection[i-1].find_all()):
  6. del selection[i-1]
  7. print(selection)

字符串
结果是:

  1. [<p>Some <b>text</b></p>, <p>Some <i>text</i> different than <div>before</div></p>]


另一种选择是,如果可以识别出一组阻碍您实际方法的标记,则首先将其unwrap()-认为这也是@Andrej Kesely要求一些特定标记的原因。

展开查看全部
2o7dmzc5

2o7dmzc52#

另一个解决方案,灵感来自@HedgeHog的回答:

  1. from bs4 import BeautifulSoup
  2. test_doc = BeautifulSoup(
  3. """<html><h1>Title</h1><p>Some <b>text</b></p><div><p>Some <i>text</i> different than <div>before</div></p></div>""",
  4. "html.parser",
  5. )
  6. tags = test_doc.find_all(lambda tag: "Some text" in tag.text)
  7. out = []
  8. while tags and (t := tags.pop()):
  9. while tags and t in tags[-1]:
  10. tags.pop()
  11. out.append(t)
  12. print(out)

字符串
印刷品:

  1. [<p>Some <i>text</i> different than <div>before</div></p>, <p>Some <b>text</b></p>]

展开查看全部
pengsaosao

pengsaosao3#

下面是使用lxmlxpath的方法,它也涵盖了预期文本包含在单个节点中的情况。

  1. from lxml import etree
  2. xml = """<html><h1>Title</h1>
  3. <div id="target">
  4. <div>Some <div><div><span><b>text</b></span></div></div></div>
  5. <div>Some <b>another text</b></div>
  6. <p>Some <i>text</i> different than <div>before</div></p>
  7. <em>Some text</em>
  8. </div>
  9. </html>"""
  10. root = etree.fromstring(xml)
  11. ele = root.xpath('//div[@id="target"]//*[(./text()="Some " and .//*[1]/text()="text") or ./text()="Some text"]')
  12. print(ele)

字符串
.//*[1]/text()="text"]查找包含预期字符串的上下文节点的第一个后代。它区分大小写,因此./text()="some "不会找到任何内容。
给定样品的结果

  1. [<Element div at 0x7f2d65eef6c0>, <Element p at 0x7f2d65eef700>, <Element em at 0x7f2d65eef740>]


从找到的元素中提取内容

  1. print([[t for t in e.xpath('descendant-or-self::text()')] for e in ele])


结果

  1. [['Some ', 'text'], ['Some ', 'text', ' different than ', 'before'], ['Some text']]

展开查看全部

相关问题