Python中的反向引用:HTML字符串的findall()方法输出

时间:2013-09-25 18:41:26

标签: python html regex python-2.7 backreference

我正在尝试在Python中学习一些正则表达式。以下内容不会产生我预期的输出:

with open('ex06-11.html') as f:
    a = re.findall("<div[^>]*id\\s*=\\s*([\"\'])header\\1[^>]*>(.*?)</div>", f.read())
    # output: [('"', 'Some random text')]

我期待的输出(相同的代码,但没有反向引用):

with open('ex06-11.html') as f:
    print re.findall("<div[^>]*id\\s*=\\s*[\"\']header[\"\'][^>]*>(.*?)</div>", f.read())
    # output: ['Some random text']

问题实际上归结为:为什么我的第一个输出中有一个引号,但不是在我的第二个输出中?我认为([abc]) ... //1 == [abc] ... [abc]。我不对吗?

3 个答案:

答案 0 :(得分:7)

来自re.findall上的文档:

  

如果模式中存在一个或多个组,则返回组列表;如果模式有多个组,这将是一个元组列表。

如果您希望返回整个匹配,请删除捕获组或通过在打开paren之后添加?:将其更改为非捕获组。例如,您可以将正则表达式中的(foo)更改为(?:foo)

当然,在这种情况下,您需要捕获组进行反向引用,因此最好的办法是保留当前的正则表达式,然后使用re.finditer()的列表推导来获取仅第二组的列表:< / p>

regex = re.compile(r"""<div[^>]*id\s*=\s*(["'])header\1[^>]*>(.*?)</div>""")
with open('ex06-11.html') as f:
    a = [m.group(2) for m in regex.finditer(f.read())

有几个附注,你应该考虑使用像BeautifulSoup这样的HTML解析器而不是正则表达式。如果需要在字符串中包含单引号或双引号,还应使用三引号字符串,并在编写正则表达式时使用raw string literals,这样就不需要转义反斜杠。

答案 1 :(得分:4)

行为清楚地记录在案。见re.findall

  

返回字符串中pattern的所有非重叠匹配,作为字符串列表。从左到右扫描字符串,并按找到的顺序返回匹配项。

     

如果模式中存在一个或多个组,则返回组列表;如果模式有多个组,这将是一个元组列表。结果中包含空匹配,除非它们触及另一场比赛的开头。

因此,如果您的正则表达式模式中有一个捕获组,那么findall方法将返回一个元组列表,其中包含特定匹配的所有捕获组以及group(0)

因此,要么使用非捕获组 - (?:[\"\']),要么根本不使用任何组,如第二种情况。

P.S:使用raw string literals表示你的正则表达式,以避免逃避反斜杠。另外,在循环外编译你的正则表达式,这样就不会在每次迭代时重新编译。请使用re.compile

答案 2 :(得分:0)

当我问这个问题时,我刚开始使用正则表达式。我完全阅读了docs,我只想分享我发现的内容。

首先,RohitF.J建议使用原始字符串(使正则表达式更易读,更不容易出错)并使用 re.compile 即可。要匹配id为“header”的HTML字符串:

s = "<div id='header'>Some random text</div>"

我们需要一个正则表达式:

p = re.compile(r'<div[^>]*id\s*=\s*([\"\'])header\1[^>]*>(.*?)</div>')

在正则表达式的Python实现中,通过将正则表达式的一部分括在括号(...)中来创建捕获组。捕获组捕获它们匹配的文本范围。它们也是反向引用所必需的。所以在上面我的正则表达式中,我有两个捕获组:([\"\'])(.*?)。第一个需要使反向引用\1成为可能。然而,使用反向引用(以及它们引用回捕获组的事实)会产生后果。正如在此问题的其他答案中所指出的,当在我的模式findall上使用p时,findall将返回所有组的匹配并将它们放入元组列表中:

print p.findall(s)
# [("'", 'Some random text')]

由于我们只想要HTML标记之间的纯文本,因此这不是我们正在寻找的输出。

(可以说,我们可以使用:

print p.findall(s)[0][1]
# Some random text

但这可能有点人为。)

因此,为了仅返回HTML标记之间的文本(由第二组捕获),我们在group()上使用p.search()方法:

print p.search(s).group(2)
# Some random text

我完全清楚除了最简单的HTML之外的所有HTML都不应该由正则表达式处理,而是应该使用解析器。但这只是一个教程示例,让我掌握Python中正则表达式的基础知识。