使用正则匹配HTML中标题后的项目

0 投票
5 回答
1408 浏览
提问于 2025-04-16 06:03

我遇到了一些麻烦,想用正则表达式提取一些内容,但感觉这应该是个简单的事情。我在StackOverflow上没找到类似的问题,如果有的话,请告诉我。给你看一下下面的HTML代码:

<h1 class="title">标题一</h1><p><a href="#">40.5</a><a href="#">31.3</a></p>

<h1 class="title alternate">标题二</h1><p><a href="#">12.1</a><a href="#">82.0</a></p>

(这些代码是在一个更大的文档中,提取的内容可能会跨越多行)

我想写一个正则表达式,找到每个H1标签后第一个P标签中的A标签里的文本。这个正则表达式会在一个循环中使用,这样我可以传入标题,来获取后面的内容。

<a[^>]*>([0-9.]+?)</a> 这个表达式显然可以匹配所有A标签中的内容(而且A标签不能嵌套,所以这样是可以的),但我无法把它们和H1标签关联起来。

.+标题一.+<a[^>]*>([0-9.]+?)</a></p> 这个表达式不行。

我尝试过用反向查找,像这样:

(?<=标题一.+)<a[^>]*>([0-9.]+?)</a></p> 还有一些变体,但反向查找只适用于固定宽度的匹配(在这里不适用)。

为了提供一些背景,这个正则表达式会在Python的正则引擎中使用。我知道正则表达式不一定是最好的解决方案,所以如果有其他方法,比如用DOM之类的,也非常欢迎提供建议 :)


更新

为了更清楚,我想得到以下结果:

{"标题一": ["40.5", "31.3"], "标题二": ["12.1", "82.0"]}

(我并不需要帮助来构建这个字典,但这确实展示了我需要这些值和标题之间的关系)。

到目前为止,BeautifulSoup看起来是最好的选择。LXML也可能有效,因为源HTML并不是一团糟 - 至少在我感兴趣的地方,它的结构相当不错。


5 个回答

1

解决这个问题的另一个明显方法是 BeautifulSoup —— 我觉得它处理那些在网上常见的糟糕HTML非常好,既合理又优雅。

1

你说得对,正则表达式确实不适合用来匹配HTML。

不过,你的问题听起来正好适合用Beautiful Soup来解决——这是一种可以处理不太完美的HTML的解析工具。

1

这就是你想要的东西吗?

>>> from lxml import etree
>>>
>>> data = """
... <h1 class="title">Title One</h1><p><a href="#">40.5</a><a href="#">31.3</a></p>
... <h1 class="title alternate">Title Two</h1><p><a href="#">12.1</a><a href="#">82.0</a></p>
... """
>>>
>>> d = etree.HTML(data)
>>> d.xpath('//h1/following-sibling::p[1]/a/text()')
['40.5', '31.3', '12.1', '82.0']

这个解决方案使用了 lxml.etree 和一个 xpath 表达式。


更新

>>> from lxml import etree
>>> from pprint import pprint
>>>
>>> data = """
... <h1 class="title">Title One</h1><p><a href="#">40.5</a><a href="#">31.3</a></p>
... <h1 class="title alternate">Title Two</h1><p><a href="#">12.1</a><a href="#">82.0</a></p>
... """
>>>
>>> d = etree.HTML(data)
>>> #d.xpath('//h1[following-sibling::*[1][local-name()="p"]]') 
...
>>> results = {}
>>> for h in d.xpath('//h1[following-sibling::*[1][local-name()="p"]]'):
...   r = results.setdefault(str(h.text),[])
...   r += [ str(x) for x in h.xpath('./following-sibling::*[1][local-name()="p"]/a/text()') ]
...
>>> pprint(results)
{'Title One': ['40.5', '31.3'], 'Title Two': ['12.1', '82.0']}

现在使用了条件来向前查看,这个方法应该可以遍历那些紧接着 <p> 标签的 <h1> 标签。(我记得 tag.text 不是普通字符串,所以我把它显式转换成字符串,这样在处理时就不会出问题了。)

撰写回答