如何根据正则表达式提取HTML标签

1 投票
2 回答
1331 浏览
提问于 2025-04-17 12:42

我想提取每一个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>

我可能应该尝试用正则表达式来捕捉每一个在开标签和闭标签之间的匹配,比如“<>”。不过,我希望能够根据这些匹配结果来遍历解析后的树,这样我就能找到兄弟节点、父节点或者“下一个元素”。在上面的例子中,这意味着我想得到<head>*</head>,或者一旦我知道它们是包含匹配的标签的父节点或兄弟节点,就可能得到<h2>*</h2>

我试过BeautifulSoup,但我觉得它在你已经知道想找什么标签或者根据内容来找的时候比较有用。在这种情况下,我想先找到匹配的标签,把这个匹配作为起点,然后像BeautifulSoup和其他HTML解析器那样遍历树。

有什么建议吗?

2 个回答

1

根据以下条件:

  • 匹配必须发生在标签的属性值上
  • 匹配必须发生在标签的直接子节点中的文本节点上

你可以使用Beautiful Soup这个库:

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>
2

使用 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

撰写回答