1

I am trying to learn some regular expressions in Python. The following does not produce the output I expected:

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

The output I was expecting (same code, but without the backreference):

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

The question really boils down to: why is there a quotation mark in my first output, but not in my second? I thought that ([abc]) ... //1 == [abc] ... [abc]. Am I incorrect?

4

3 回答 3

7

从上的文档re.findall

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

如果要返回整个匹配项,请删除捕获组或通过?:在开始括号后添加将它们更改为非捕获组。例如,您可以将(foo)正则表达式更改为(?:foo).

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

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 解析器而不是正则表达式。如果您需要在字符串中包含单引号或双引号,您还应该使用三引号字符串,并在编写正则表达式时使用原始字符串文字,这样您就不需要转义反斜杠。

于 2013-09-25T18:44:35.327 回答
4

该行为被清楚地记录在案。见重新。findall

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

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

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

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

PS:为您的正则表达式模式使用原始字符串文字,以避免转义您的反斜杠。此外,在循环外编译你的正则表达式,这样就不会在每次迭代时重新编译。为此使用re.compile

于 2013-09-25T18:44:20.567 回答
0

当我问这个问题时,我只是从正则表达式开始。从那以后,我已经完全阅读了文档,我只想分享我发现的内容。

首先,根据RohitFJ的建议,使用原始字符串(使正则表达式更具可读性且不易出错)并事先使用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在我的模式上使用时pfindall将返回所有组的匹配项并将它们放入元组列表中:

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

因为我们只想要 HTML 标签之间的纯文本,所以这不是我们要寻找的输出。

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

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

但这可能有点做作。)

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

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

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

于 2013-10-09T09:23:05.870 回答