regex - 来自网络爬虫函数的这个正则表达式背后的逻辑是什么: ("^(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 还是很陌生。谢谢
解决方案
这是一个regexr
解释语法的链接。
了解奇怪的符号
^
匹配字符串的开头。
$
匹配字符串的结尾。
这有很大的不同,因为它会匹配整个字符串,而不仅仅是一个子字符串。
(http|www)
匹配http
或www
(?!
...)
就是所谓的负前瞻。它“指定了一个在主表达式之后无法匹配的组(如果匹配,则丢弃结果)”。
例如,t(?!s)
匹配第一个但不匹配第二个,因为前瞻在第二个之后找到t
了一个。streets
t
s
t
.
将匹配任何字符(字母数字、符号,换行符除外'\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] 实例”。现在您明白为什么美元符号很重要了,因为它强制匹配整个字符串。
推荐阅读
- c - 当浮点数是次正规数时,frac 位会发生什么?
- python-3.x - 如何设置索引变量的值?- 皮莫
- sql - 为没有子查询的每个组选择最大,最小行,大小写相同
- javascript - 在 TypeScript 中将 props 传递给具有正确类型的“react-bootstrap”组件
- r - 无法使用 dplyr::select 选择列
- r - 从 tibble 列表中提取到单个数据框对象
- retool - Retool:创建侧边栏以动态更改容器窗口的组件
- java - 通过 VPC 对等连接到 RDS 时出现 Spring Boot UnknownHostException
- c# - 如何使用计时器触发器将时区传递给 Azure 函数
- php - PHP:四舍五入的数字不作为数字核对?