【Python3 爬虫学习笔记】基本库的使用 9—— 正则表达式 2

1.1 匹配目标

如果想从字符串中提取出一部分内容,可以用()括号将想提取的子字符串括起来。()实际上标记了一个子表达式的开始和结束位置,被标记的每个子表达式会一次对应一个分组,调用group()方法传入分组的索引即可获取提取的结果。示例如下:

import re

content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^Hello\s(\d+)\sWorld', content)
print(result)
print(result.group())
print(result.group(1))
print(result.span())

我们相把字符串中的1234567提取出来,此时可买将数字部分的正则表达式用()括起来,然后调用了group(1)获取匹配结果。
运行结果如下:

<_sre.SRE_Match object; span=(0, 19), match='Hello 1234567 World'>
Hello 1234567 World
1234567
(0, 19)

可以看到,我们成功得到了1234567。这里用的是group(1),它与group()有所不同,后者会输出完整的匹配结果,而前者会输出第一个被()包围的匹配结果。假如正则表达式后面还有()包括的内容,那么可以依次用group(2)、group(3)等来获取。

1.2 通用匹配

.(点)可以匹配任意字符(除换行符),*(星)代表匹配前面的字符无限次,所以它们组合在一起可以匹配任意字符了。

import re

content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^Hello.*Demo$', content)
print(result)
print(result.group())
print(result.span())

这里我们将中间部分直接省略,全部用.*来代替,最后加一个结尾字符串就好了。

<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
Hello 1234567 World_This is a Regex Demo
(0, 40)

可以看到,group()方法输出了匹配的全部字符串,也就是我们写的正则表达式匹配到了目标字符串的全部内容;span()方法输出(0,41),这是整个字符串的长度。

1.3 贪婪与非贪婪

在贪婪匹配下,(点星) 会匹配尽可能多的字符。正则表达式中(点星)后面是\d+,也就是至少一个数字,并没有指定具体多少个数字,因此,.*就尽可能匹配多的字符,这里就把123456匹配了,给\d+留下一个可满足条件的数字7,最后得到的内容就只有数字7了。
非贪婪的写法是(点星),多了一个?,那么它可以达到怎样的效果?

import re

content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^Hello.*?(\d+).*Demo$', content)
print(result)
print(result.group(1))

这里我们只是将第一个(点星)改成(点星?),转变为非贪婪匹配。结果如下:

<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
1234567

此时就可以成功获取1234567了。贪婪匹配是尽可能匹配多的字符,非贪婪匹配就是尽可能匹配少的字符。当(点星?)匹配到Hello后面的空白字符时,再往后的字符就是数字了,而\d+恰好可以匹配,那么这里(点星?)就不再进行匹配,交给\d+去匹配后面的数字。所以这样(点星?)匹配了尽可能少的额字符,\d+的结果就是1234567。
所以说,在做匹配的时候,字符串中间尽可能使用非贪婪匹配,也就是用(点星?)来代替(点星),以免出现匹配确实的情况。
但这里需要注意,如果匹配的结果在字符串,(点星?)就有可能匹配不到任何内容,因为它会匹配尽可能少的字符。

import re

content = 'http://weibo.com/comment/kEraCN'
result1 = re.match('http.*?comment/(.*?)',content)
result2 = re.match('http.*?comment/(.*)',content)
print('result1', result1.group(1))
print('result2', result2.group(1))

运行结果如下:

result1
result2 kEraCN

可以观察到,.*?没有匹配到任何结果,而(点星)则尽可能匹配多的内容,成功得到了匹配结果。

扫描二维码关注公众号,回复: 3610611 查看本文章

1.4 修饰符

正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。

import re

content = '''Hello 1234567 World_This
is a Regex Demo
'''
result = re.match('^He.*?(\d+).*?Demo$',content)
print(result.group(1))

我们在字符串中加了换行符,正则表达式还是一样的,用来匹配其中的数字。运行结果如下:

AttributeError                            Traceback (most recent call last)
<ipython-input-6-379f50fcebe7> in <module>()
      5 '''
      6 result = re.match('^He.*?(\d+).*?Demo$',content)
----> 7 print(result.group(1))

AttributeError: 'NoneType' object has no attribute 'group'

运行直接报错,也就是说正则表达式没有匹配到这个字符串,返回结果为None,而我们又调用了group()方法导致AttributeError。
. 匹配的是除换行符之外的任意字符,当遇到换行符是,.*?就不能匹配了,所以导致匹配失败。这里只需要一个修饰符re.S,即可修正这个错误:

result = re.match('^He.*?(\d+).*?Demo$',content,re.S)

这个修饰符的作用使.匹配包括换行符在内的所有字符。便能匹配到数字:1234567。
这个re.S在网页匹配中经常用到。因为HTML节点经常会有换行,加上它,就可以匹配节点与节点之间的换行了。
另外,还有一些修饰符,在必要的情况下也可以使用:

修饰符 描述
re.I 使匹配对大小写不敏感
re.L 使本地化识别(locale-aware)匹配
re.M 多行匹配,影响^和$
re.S 使.匹配包括换行在内的所有字符
re.U 根据Unicode字符集解析字符。这个标志影响\w、\W、\b和\B
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解

在网页匹配中,较为常用的有re.S和re.I。

1.5 转义匹配

当遇到用于正则匹配模式的特殊字符时,在前面加反斜线转义一下即可。例如.就可以用.来匹配。

import re
content = '(百度)www.baidu.com'
result = re.match('\(百度\)www\.baidu\.com',content)
print(result)

运行结果:

<_sre.SRE_Match object; span=(0, 17), match='(百度)www.baidu.com'>

猜你喜欢

转载自blog.csdn.net/htsait4113/article/details/82765339