【问题标题】:How to retrieve a HTML tag based on a regular expression如何根据正则表达式检索 HTML 标签
【发布时间】:2012-03-02 07:45:51
【问题描述】:

我正在尝试提取每个 HTML 标记,包括正则表达式的匹配项。例如,假设我想获取包含字符串“name”的每个标签,并且我有一个这样的 HTML 文档:

<html>
  <head>
    <title>This tag includes 'name', so it should be retrieved</title>
  </head>
  <body>
    <h1 class="name">This is also a tag to be retrieved</h1>
    <h2>Generic h2 tag</h2>
  </body>
</html>

可能,我应该尝试使用正则表达式来捕获打开和关闭 "&lt;&gt;" 之间的每一个匹配项,但是,我希望能够根据这些匹配项遍历解析的树,这样我就可以获得兄弟姐妹或父母或“下一个元素”。在上面的示例中,一旦我知道他们是包含匹配项的标签的父母或兄弟姐妹,这相当于获得&lt;head&gt;*&lt;/head&gt;&lt;h2&gt;*&lt;/h2&gt;

我尝试了 BeautifulSoap,但在我看来,当您已经知道要查找的标签类型或基于其内容时,它很有用。在这种情况下,我想先获得一个匹配项,以该匹配项为起点,然后像 BeautifulSoap 和其他 HTML 解析器那样导航树。

建议?

【问题讨论】:

  • 在 Html 上使用正则表达式是困难的。我不建议你走这条路。你想用 Html 做什么?见这篇文章:codinghorror.com/blog/2009/11/parsing-html-the-cthulhu-way.html
  • 我认为您并没有真正彻底了解这一点。怎么样:&lt;input name="foo"/&gt;&lt;p&gt; My &lt;b&gt;name&lt;/b&gt; is beerbajay&lt;/p&gt;?您期望应该返回什么?
  • @beerbajay 不,如果检索该输入标签就可以了(因为它包含“名称”。显然,我的真实示例没有使用“名称”作为匹配项。
  • @AdamD 感谢您的链接:-)。我正在尝试进行匹配并获取该匹配附近的一些内容,以作为进一步分析的上下文。使用 HTML 标签让它变得更加优雅,即使它变得更加困难。

标签: python regex beautifulsoup parse-tree


【解决方案1】:

使用lxml.html。这是一个很棒的解析器,它支持xpath,可以轻松表达你想要的任何东西。

下面的例子使用了这个 xpath 表达式:

//*[contains(text(),'name']/parent::*/following-sibling::*[1]/*[@class='name']/text()

这意味着,用英语:

找到任何在其文本中包含单词'name' 的标签,然后获取 父级,然后是下一个兄弟级,并在其中找到该类的任何标记 'name' 最后返回那个文本内容。

运行代码的结果是:

['This is also a tag to be retrieved']

这是完整的代码:

text = """
<html>
  <head>
    <title>This tag includes 'name', so it should be retrieved</title>
  </head>
  <body>
    <h1 class="name">This is also a tag to be retrieved</h1>
    <h2>Generic h2 tag</h2>
  </body>
</html>
"""

import lxml.html
doc = lxml.html.fromstring(text)
print doc.xpath('//*[contains(text(), $stuff)]/parent::*/'
    'following-sibling::*[1]/*[@class=$stuff]/text()', stuff='name')

必读,“请不要使用正则表达式解析 HTML”答案在这里: https://stackoverflow.com/a/1732454/17160

【讨论】:

  • 哦,看起来不错(尽管语法很吓人)。只是为了确定,我想要的是“找到任何包含单词'name'的标签(我不在乎它是在 TextNode 中还是在属性中,无论如何),然后获取父级,然后是下一个兄弟. 对包含单词“name”的任何其他标签做同样的事情。所以我认为我不需要匹配标签中带有“class='name'”的文本的最后一部分。并且 text() 适用于标签,不仅仅是它的TextNode,对吧?
  • 嗯,看起来像 "contains(text(), $stuff)..." 只获取第一个标签。它也应该检索相同的内容,因为其中包含“名称”。我试过“contains(*, $stuff)...”,但我只得到前两个标签(html、head)。你知道如何获得第二个标签吗?
  • 我认为这解决了它:doc.xpath("//*[contains(text(),'name')]|//*[@*='name']")跨度>
【解决方案2】:

鉴于以下条件:

  • 匹配必须出现在标签的属性值中
  • 匹配必须出现在作为标记的直接子节点的文本节点中

你可以用漂亮的汤:

from bs4 import BeautifulSoup
from bs4 import NavigableString
import re

html = '''<html>
  <head>
    <title>This tag includes 'name', so it should be retrieved</title>
  </head>
  <body>
    <h1 class="name">This is also a tag to be retrieved</h1>
    <h2>Generic h2 tag</h2>
  </body>
</html>'''

soup = BeautifulSoup(html)
p = re.compile("name")

def match(patt):
    def closure(tag):
        for c in tag.contents:
            if isinstance(c, NavigableString):
                if patt.search(unicode(c)):
                    return True
        for v in tag.attrs.values():
            if patt.search(v):
                return True
    return closure

for t in soup.find_all(match(p)):
    print t

输出:

<title>This tag includes 'name', so it should be retrieved</title>
<h1 class="name">This is also a tag to be retrieved</h1>

【讨论】:

  • 感谢您的回答。你确定这个代码吗?不应该是 'findAll' 而不是 'find_all'?不过,我收到以下错误:“AttributeError:'list' 对象没有属性'values'”。我认为您没有将任何值传递给闭包(标签)。
  • 对不起,我没提,代码使用bs4,即BeautifulSoup4,新发布的。
  • 是这样吗?我将“从 bs4 导入 NavigableString”更改为“从 BeautifulSoup 导入 NavigableString”,它没有抱怨,但 AttributeError 仍然存在
  • 您可以安装 BeautifulSoup4 并按原样使用代码:easy_install beautifulsoup4
  • 不幸的是,我不能。我坚持使用 Python 2.7,而 bs4 仅适用于 Python 3+,对吗?
猜你喜欢
  • 2022-08-19
  • 2021-09-30
  • 1970-01-01
  • 2014-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多