为什么这个Python正则表达式的负向预测不生效?

4 投票
2 回答
2740 浏览
提问于 2025-04-29 14:14

我正在尝试使用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
^.*?/b-\d+(?:(?!View%20All).)*$

演示链接

或者更快一些

^.+?/b-\d+(?:[^V]+|V(?!iew%20All))*$
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

在这里,字符串中的 ?$ 确定了负向断言开始的位置。

撰写回答