【问题标题】:Backreferencing in Python: findall() method output for HTML stringPython 中的反向引用:HTML 字符串的 findall() 方法输出
【发布时间】:2013-10-01 12:07:09
【问题描述】:

我正在尝试在 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]。我错了吗?

【问题讨论】:

    标签: python html regex python-2.7 backreference


    【解决方案1】:

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

    【讨论】:

    • 那么,如果我使用(["']) ... \1,为什么会有多个组,而不是当我只输入["'] ... ["'] 时。如果答案很明显,我很抱歉,但我整天都在为此烦恼。顺便说一句,谢谢,您的代码运行良好:)
    【解决方案2】:

    行为有明确的记录。见re.findall

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

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

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

    所以,要么您使用非捕获组 - (?:[\"\']),要么根本不使用任何组,就像您的第二种情况一样。

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

    【讨论】:

      【解决方案3】:

      当我问这个问题时,我只是从正则表达式开始。我已经完全阅读了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 成为可能。然而,使用反向引用(以及它们引用回捕获组的事实)会产生后果。正如该问题的其他答案中所指出的,当在我的模式 p 上使用 findall 时,findall 将返回所有组的匹配项并将它们放入元组列表中:

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

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

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

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

      但这可能有点做作。)

      所以为了只返回 HTML 标签之间的文本(被第二组捕获),我们在 p.search() 上使用 group() 方法:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-09-08
        • 2013-12-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-07
        • 1970-01-01
        相关资源
        最近更新 更多