首页 > 解决方案 > 来自网络爬虫函数的这个正则表达式背后的逻辑是什么: ("^(http|www)((?!("+linkParse+")).)*$")

问题描述

我正在研究网络抓取教科书中的一个例子。网络爬虫的功能是获取网页上的外部链接。

我以一种我能理解的更简单的格式重新定义了函数,但是一行正则表达式让我感到困惑。整个函数写在下面。

url = "http://oreilly.com"

url_parse = urlparse(url)

external_links = set()

def scrape_external(url):
    html = urlopen(url)
    bsObj = BeautifulSoup(html.read(), "lxml")
    linkParse = url_parse.netloc
    #this is the line I need some clarity below
    externalLinks = bsObj.findAll("a",{"href": re.compile("^(http|www)((?!"+linkParse+").)*$")})
    for i in externalLinks:
        if "href" in i.attrs:
            link = i.attrs['href']
            external_links.add(link)
        
    print(external_links)

scrape_external(url)

根据我自己的理解,正则表达式行的意思是“只匹配 http 或 www 当它后面没有 home url 时”。但我需要更深入地了解整个事情的工作原理或背后的逻辑......我知道符号的含义,但我在将整个事情放在一起时遇到了一些麻烦。特别是“* 和 $”符号。

例如,为什么我需要将美元符号放在末尾,为什么当我删除它时它会对我的结果产生如此大的影响。

这是我在这里的第一个问题,我对 python 还是很陌生。谢谢

标签: regexpython-3.xweb-scraping

解决方案


这是一个regexr解释语法的链接。

了解奇怪的符号

^匹配字符串的开头。

$匹配字符串的结尾。

这有很大的不同,因为它会匹配整个字符串,而不仅仅是一个子字符串。

(http|www)匹配httpwww

(?!...)就是所谓的负前瞻。它“指定了一个在主表达式之后无法匹配的组(如果匹配,则丢弃结果)”。

例如,t(?!s)匹配第一个但不匹配第二个,因为前瞻在第二个之后找到t了一个。streetstst

.将匹配任何字符(字母数字、符号,换行符除外'\n')。

*将匹配上述的 0 个或多个实例,负前瞻

[我相信]linkParse结果是oreilly.com

((?!(oreilly.com)).)*只要后面没有. ,就会一直匹配字符oreilly.com

测试正则表达式

所以解析正则表达式,仔细检查上下文,并在我的 IDLE 上尝试这个,我们可以观察到正则表达式会匹配外部链接

>>> import re
>>> r = re.compile("^(http|www)((?!oreilly.com).)*$")
>>> m = r.match('https://www.google.com')
>>> m
<re.Match object; span=(0, 22), match='https://www.google.com'>
>>> m = r.match('https://www.google.com/food')
>>> m
<re.Match object; span=(0, 27), match='https://www.google.com/food'>
>>> m = r.match('https://oreilly.com/tests')
>>> m
>>> type(m)
<class 'NoneType'>
>>> m = r.match('https://oreally.com')
>>> m
<re.Match object; span=(0, 19), match='https://oreally.com'>

正则表达式不会匹配任何包含 的链接oreilly.com,因此保证只返回外部链接。但是,它不会匹配包含 oreilly.com. 例如:

>>> m = r.match('https://www.google.com/search?q=memes+oreilly.com')
>>> m
>>> type(m)
<class 'NoneType'>

因此,人们可能会质疑它与外部链接匹配的程度。

我不确定 BeautifulSoup 如何解析正则表达式,但我猜它可能是相似的。

$最后的美元符号

您还想知道最后的美元符号。这是一个无意匹配的内部链接的示例。

>>> r = re.compile("^(http|www)((?!oreilly.com).)*")
>>> m = r.match('https://oreilly.com/tests')
>>> m
<re.Match object; span=(0, 8), match='https://'>

为什么?这是因为正则表达式匹配https://,这意味着它0匹配((?!oreilly.com).). 这是有道理的,因为记住,*意思是“匹配 0 个或多个 [an expression] 实例”。现在您明白为什么美元符号很重要了,因为它强制匹配整个字符串。


推荐阅读