Python正则表达式:(.+)和(.+?)之间的区别

8
我对正则表达式和Python的urllib不太熟悉。我通过在线教程学习了网络爬虫并使用了以下代码。在学习了正则表达式后,我认为我可以在我的正则表达式中使用(.+)代替(.+?),但是结果我错了。我打印出来的HTML代码比我想要的多得多。我以为我已经掌握了正则表达式,但现在我感到困惑。请解释一下这两个表达式之间的区别,以及为什么它会抓取那么多的HTML。谢谢!
附注:这是一个星巴克股票报价爬虫。
import urllib
import re

url = urllib.urlopen("http://finance.yahoo.com/q?s=SBUX")
htmltext = url.read()
regex = re.compile('<span id="yfs_l84_sbux">(.+?)</span>')
found = re.findall(regex, htmltext)

发现打印


可能是正则表达式中.*?和.*的区别的重复问题。 - Chris Laplante
3
FYI,使用正则表达式解析 HTML 是个不好的主意。 - Liam McInroy
好的,冷静点伙计,我只是把这个当作学习工具。我对正则表达式和urllib都很新,所以我认为这将是一个不错的沙盒练习。 - user3739620
3个回答

11

.+是贪婪模式--它会匹配尽可能多的字符,然后退回到仅匹配所需的字符。

.+?不是--它在第一次机会时停止匹配。

举个例子:

假设有这段HTML:

<span id="yfs_l84_sbux">foo bar</span><span id="yfs_l84_sbux2">foo bar</span>

这个正则表达式匹配整个字符串:

<span id="yfs_l84_sbux">(.+)<\/span>
它一直匹配到末尾,然后“退回”一个</span>,但是正则表达式的其余部分仍匹配该最后一个</span>,因此完整的正则表达式匹配整个HTML块。
但是这个正则表达式在第一个</span>处停止匹配:
<span id="yfs_l84_sbux">(.+?)<\/span>

1
你太棒了!解释易懂。或许你也可以向我解释一下。为什么在我的情况下,re.search返回一个看起来奇怪的字符串,而re.findall给我想要的数据? - user3739620
你可能会发现这个答案有帮助。findall返回捕获的组列表,而search返回匹配的所有内容。在你的情况下,这包括HTML标签。我在Python方面还是一个初学者,但我相信如果你从正则表达式中删除括号,两种方法将得到相同的结果。 - elixenide

3

? 是一个非贪婪的修饰符号。默认情况下 * 是一个贪婪的重复运算符 - 它会尽可能地吞掉所有内容;当被 ? 修改后,它变成了非贪婪模式,并且只会消耗满足条件的最小数量。

所以对于:

<span id="yfs_l84_sbux">want</span>text<span id="somethingelse">dontwant</span>

.*?</span>会匹配want,然后匹配</span> - 这样使用最少的重复次数满足正则表达式,得到匹配结果为<span id="yfs_l84_sbux">want</span>。然而,.*会尝试看看它是否可以吃更多 - 它会去找到另一个</span>,使用.*?匹配want</span>text<span id="somethingelse">dontwant,导致你得到的结果比你想要的多得多。


*?实际上,原帖使用的是+。 - Unihedron
唉,同样的推理。 - Amadan

1

(.+)是贪婪的。它尽可能地获取并在需要时返回。

(.+?)是非贪婪的。它尽可能少地获取。

参见:

delegate

[delegate] /^(.+)e/
[de]legate /^(.+?)e/

此外,比较“正则表达式调试器日志”这里这里将更有效地展示非贪婪模式的作用。

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