为什么这个Python正则表达式的负向预测不生效?
我正在尝试使用BeautifulSoup收集一组网址,这些网址有一个非常具体的要求。我想收集的网址必须包含 /b-\d+
(也就是 /b-
后面跟着一串数字)。不过,我想忽略所有包含 View%20All
的网址,即使它们里面也有 /b-\d+
。
这里有一些网址的例子:
1. http://www.foo.com/bar/b-12312903?sName=View%20All
2. http://www.foo.com/bar/b-832173712873?sName=View%20All
3. http://www.foo.com/bar/b-1208313109283129
4. http://www.foo.com/bar/b-2198123371239489?adCell=W3
根据上面的例子,我想收集的有效网址是第3个和第4个。我尝试过使用不同的负向前瞻正则表达式,但都没有成功:
{"href" : re.compile(r"\/b-\d+.+(?!View\%20All)")}
{"href" : re.compile(r"^.+\/b-\d+.+(?!View\%20All$)")}
有人能告诉我我哪里出错了吗?
2 个回答
1
4
{"href" : re.compile(r"\/b-\d+.+(?!View\%20All)")}
{"href" : re.compile(r"^.+\/b-\d+.+(?!View\%20All$)")}
你哪里搞错了呢?
当我们使用 (?!View\%20All)
时,它的意思是紧接着前面模式 .+
后面不能跟 View\%20All
。
实际上,这意味着这个前瞻检查总是成立。
为了说明这一点,我们来看看每个模式匹配到的内容。
http://www.foo.com/bar/b-12312903?sName=View%20All
/b-
是显而易见的。
\d
匹配 12312903
。
现在问题来了,
.+
匹配任何内容,这样就使得负向断言 (?!View\%20All)
成功。
也就是说,
.
匹配了 ?s
,而剩下未匹配的字符串是 sName=View%20All
,它在开头位置 s
不匹配 (?!View\%20All)
,因此总是成功匹配第一行和第二行。
这里有个演示,可以让你更清楚地理解。
解决办法?
在使用前瞻断言时,修正检查开始的位置。
比如使用这样的正则表达式:
(\/b-\d+)(\?|$)(?!sName=View\%20All)
这将匹配到第3和第4个内容,因为
http://regex101.com/r/aS5yS2/1
在这里,字符串中的 ?
或 $
确定了负向断言开始的位置。