HTML文本中链接的正则表达式

7
我希望这不是一个 RTFM(阅读完整说明) 的问题。 我正试图编写一个提取标准 HTML 网页中链接的 Python 脚本(<link href... 标签)。 我在网上搜索匹配的正则表达式,并找到了许多不同的模式。是否有任何一致的、标准的正则表达式可用于匹配链接?
亚当
更新: 实际上,我正在寻找两个不同的答案: 1.解析 HTML 链接的库解决方案。Beautiful Soup 似乎是一个好的解决方案(感谢 Igal Serban 和 cletus!) 2.链接是否可以使用正则表达式定义?
8个回答

17

使用HTML处理正则表达式会很混乱。只需使用像Beautiful Soup这样的DOM解析器。


不,HTML不能用正则表达式来描述。它更加复杂。更糟糕的是,浏览器允许接受无效的HTML,因此网站会发送无效的HTML。 - S.Lott
我发誓这个问题被提出的足够频繁,值得在常见问题解答中置顶。 - annakata

8

像其他人建议的那样,如果没有实时性能要求,BeautifulSoup是一个很好的解决方案:

import urllib2
from BeautifulSoup import BeautifulSoup

html = urllib2.urlopen("http://www.google.com").read()
soup = BeautifulSoup(html)
all_links = soup.findAll("a")

关于第二个问题,是的,HTML链接应该被定义明确,但你实际遇到的HTML很可能不是标准的。BeautifulSoup的优势在于它使用类似浏览器的启发式方法来尝试解析非标准、格式错误的HTML,这是你可能真正遇到的情况。
如果你确定要处理标准的XHTML,你可以使用(更快的)XML解析器,如expat。
正则表达式由于上述原因(解析器必须维护状态,而正则表达式无法做到这一点),永远不会成为一个通用的解决方案。

5

没有。

你可以考虑使用Beautiful Soup。你可以称它为解析html文件的标准。


4

链接应该是一个明确定义的正则表达式吗?

不,[X]HTML通常不能通过正则表达式进行解析。考虑以下示例:

<link title='hello">world' href="x">link</link>
<!-- <link href="x">not a link</link> -->
<![CDATA[ ><link href="x">not a link</link> ]]>
<script>document.write('<link href="x">not a link</link>')</script>

这只是一些随机的有效示例,如果你需要处理现实世界中的标签混乱的HTML,那么就有数百万种格式错误的可能性。

如果你知道并可以依赖目标页面的确切输出格式,你可以使用正则表达式。否则,它完全是爬取网页的错误选择。


你所有的例子都可以被正则表达式解析(不是说最后一个无效)。XML SAX解析器(OP需要的)只是由RE定义的语言的词法分析器。 “格式错误的可能性”对此没有任何影响。 - jpalecek

3

链接难道不应该是一个定义明确的正则表达式吗?这是一个相当理论性的问题,

我同意PEZ的答案:

我认为HTML并不适合“定义明确”的正则表达式,因为它不是一种正则语言。

据我所知,任何HTML标签都可以包含任意数量的嵌套标签。例如:

<a href="http://stackoverflow.com">stackoverflow</a>
<a href="http://stackoverflow.com"><i>stackoverflow</i></a>
<a href="http://stackoverflow.com"><b><i>stackoverflow</i></b></a>
...

因此,原则上,为了正确匹配标签,您至少必须能够匹配以下形式的字符串:
BE
BBEE
BBBEEE
...
BBBBBBBBBBEEEEEEEEEE
...

在这里,B代表标签的开头,E代表标签的结尾。也就是说,您必须能够匹配由任意数量的B和相同数量的E组成的字符串。为了做到这一点,您的匹配器必须能够"计数",而正则表达式(即有限状态自动机)无法做到这一点(为了计数,自动机需要至少一个栈)。提到PEZ的答案,HTML是上下文无关语法,而不是正则语言。


不,实际上你不需要那些。在HTML中,A标签不能嵌套,而它们内部的内容超出了你获取链接所需的范围。 - jpalecek

1

回答你的两个子问题。

  1. 我有时会对 SGMLParser(包含在核心 Python 发行版中)进行子类化,必须说这很简单。
  2. 我认为 HTML 不适合使用“定义良好”的正则表达式,因为它不是一种正则语言。

可能是这样。在我工作的地方,事情并不总是处于尖端水平。=) - PEZ
有没有关于适当的Python 3替代方案的推荐? - Adam Matan
不是很确定。也许这篇文章可以提供一些线索:http://www.boddie.org.uk/python/HTML.html - PEZ

1

这取决于HTML是如何生成的。如果有一定的控制,你可以这样做:

re.findall(r'''<link\s+.*?href=['"](.*?)['"].*?(?:</link|/)>''', html, re.I)

0
针对问题#2(链接是否应该是一个明确定义的正则表达式),答案是……不是。
HTML链接结构是递归的,就像编程语言中的括号和大括号一样。必须有相等数量的开始和结束结构,并且“链接”表达式可以嵌套在自身内部。
为了正确匹配“链接”表达式,需要使用正则表达式来计算开始和结束标记。正则表达式是有限自动机的一类。根据定义,有限自动机无法在模式中“计数”结构。需要使用语法来描述这样的递归数据结构。正则表达式无法“计数”的原因是你会看到编程语言用语法来描述,而不是正则表达式。
因此,不可能创建一个可以100%积极匹配所有“链接”表达式的正则表达式。当然有些正则表达式可以高度准确地匹配大量“链接”,但它们永远不会完美。
我最近写了一篇关于这个问题的博客文章。正则表达式的局限性

既有趣又有帮助 - 谢谢。 顺便说一下,这个问题可以通过一个下推栈自动机来解决,它比正则表达式具有更强的计算能力 - 这可以很容易地通过使用泵引理(http://en.wikipedia.org/wiki/Pumping_lemma)来证明。 - Adam Matan
不是真的。HTML中的递归结构(如表格中的表格和许多其他结构)肯定不能被正则表达式解析,但HTML中的链接和锚并不是递归的,因此您无需关心递归结构即可获取链接。 - jpalecek
@jpalecek,您是错误的。A标签绝对是递归的,因为A标签的内容可以包含另一个A标签。这可能看起来很奇怪,但它肯定是可解析的HTML。 - JaredPar
不可以在A标签内包含A标签。根据HTML 4.01 DTD: "<!ELEMENT A - - (%inline;)* -(A)", -(A)表示A标签内不能嵌套另一个A标签。XML DTD无法表达这一点,但http://www.w3.org/TR/xhtml1/#prohibitions禁止这样做。 - jpalecek
1
@jpalecek,有意思。我通常更多地从“可解析性”而不是“合法的HTML”角度来处理这些问题,因为网站往往倾向于前者。即使没有这个,您仍然可以通过在CDATA或文字字符串中嵌入来实现<a>元素的直接插入。 - JaredPar
是的,但实际上这不是“可解析”的,因为浏览器不会解析它 :-) 这是一种使语言更简单的属性,浏览器编写者利用它,所以没必要费心。关于CDATA和字面量——它们都是常规语言,所以它们不会成为正则表达式的障碍。 - jpalecek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接