re模块
re模块
-
负责处理正则表达式的模块
-
什么是正则表达式?
- 正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法。或者说:正则就是用来描述一类事物的规则。(在Python中)它内嵌在Python中,并通过 re 模块实现。正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。
-
正则表达式的使用场景
- 判断某个字符串是否符合规则 用户注册页面—表单验证
- 将符合规则的内容从一个庞大的字符串体系当中提取出来 爬虫、日志分析
-
元字符
-
字符组
-
在字符组中所有的字符都可以匹配任意一个字符位置上能出现的内容,如果在字符串中有任意一个字符是字符串的内容,那么就是符合匹配要求的项
-
格式:[],匹配中括号内的字符
import re ret = re.search('[abc]','a') print(ret) 输出: <_sre.SRE_Match object; span=(0, 1), match='a'>
-
通过上述结果表明‘a’是满足这个元字符正则表达式的。
-
-
字符
元字符 匹配结果 .
匹配除了 \n
的所有字符\w
匹配数字、字母、下划线 \s
匹配空字符,包括空格符、换行符、制表符 \d
匹配0-9的数字 \n
匹配一个换行符 \t
匹配一个制表符 \b
匹配单词的边界\b在前为匹配开头,\b在后为匹配结尾 \W
匹配除了数字、字母、下划线之外的所有字符 \S
匹配除了空字符,包括空格符、换行符、制表符的字符 \D
匹配除了0-9的数字的字符 \B
匹配单词中间包含,不匹配首尾 ^
匹配开始标识符 $
匹配结尾标识符 `a b` ()
匹配括号内的表达式,也代表一个组 [...]
匹配字符组中的字符 [^...]
匹配除了字符组中的所有字符 '\b的实际用法:判断多个单词,将以ing结尾的单词取出' import re string = "sing 1studing studing1 swimming" ret = re.findall(r"\b[a-zA-Z]*ing\b",string) print(ret) 输出: ['sing', 'swimming'] '[^...]的实际用法:取出多层算式的最内层括号的所有值,1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))' import re s = '1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))' ret = re.findall(r'\([^()]+\)',s) print(ret) 输出: ['(-40/5)', '(9-2*5/3+7/3*99/4*2998+10*568/14)', '(-4*3)', '(16-3*2)']
-
-
量词
-
量词必须跟在元字符后,并且通常只能约束一个元素
-
注意:由分组括起来的元素算一个元素
import re #程序一 ret = re.search('(as\d)*','as1as2as3') print(ret) 输出: <_sre.SRE_Match object; span=(0, 9), match='as1as2as3'> ret = re.search('(as\d)*','as1a2as3') #程序二 print(ret) 输出: <_sre.SRE_Match object; span=(0, 3), match='as1'>
- 上述的程序唯一的区别在于字符串由
‘as1as2as3’
改为了‘as1a2as3’
,但是匹配结果就发生了差异,由于程序一中匹配的字符串as1
、as2
、as3
都满足正则表达式,所以*将他们全部匹配到了,而程序二中因为删除掉了一个as2
的s所以程序中只能匹配到as1
。
量词 使用方法 *
重复0次或更多次 +
重复1次或更多次 ?
重复0次或1次 {n}
重复n次 {n,}
重复n次或更多次 {n,m}
重复n次到m次 - 上述的程序唯一的区别在于字符串由
-
-
贪婪匹配:正则会尽量多的匹配
- 默认贪婪 回溯算法
-
非贪婪匹配:会尽力少的匹配
- 默认算法 惰性算法
- .*?x 遇到x立即停止
import re #程序一 s = "<script> alog('speed.set', 'ht', +new Date); </script>" ret = re.findall('<.*>',s) print(s) 输出: ["<script> alog('speed.set', 'ht', +new Date); </script>"] import re #程序二 s = "<script> alog('speed.set', 'ht', +new Date); </script>" ret = re.findall('<.*?>',s) print(ret) 输出: ['<script>', '</script>']
-
上述输出第一个程序为贪婪匹配,第二个程序为非贪婪算法,上述的贪婪匹配算法在第一次匹配到>时并没有停止,因为根据*的量词规则,会尽可能多的去匹配字符,所以会匹配最后的那个>,而在第二个程序中添加了一个?,所以只会匹配第一个>,所以可以得出两个元素。
-
补充非贪婪匹配:
量词组 匹配规则 *?
重复任意次,但尽可能少重复 +?
重复1次或更多次,但尽可能少重复 ??
重复0次或1次,但尽可能少重复 {n,m}?
重复n到m次,但尽可能少重复 {n,}?
重复n次以上,但尽可能少重复
-
转义符
- 在Python中应用,在正则表达式和字符串前加r就可以解决所有的转移问题
re模块使用方法
1. re.findall
-
参数:正则表达式,带匹配字符串
-
返回值:一个包含所有匹配项列表
-
注意:findall会优先显示分组中的内容,如果需要结果的全部取到,必须取消分组优先,在分组前加
?:
import re ret = re.findall('\d','2138725876358') print(ret) 输出: ['2', '1', '3', '8', '7', '2', '5', '8', '7', '6', '3', '5', '8']
import re ret = re.findall('www\.(baidu|hao123)\.com','www.baidu.com') print(ret) 输出: ['baidu'] #优先匹配分组的内容 #希望显示全部内容,只需要在分组括号的首部加入?: ret = re.findall('www\.(?:baidu|hao123)\.com','www.baidu.com') print(ret) 输出: ['www.baidu.com']
2. re.search
-
参数:正则表达式,带匹配字符串
-
返回值:一个match对象,通过返回值.group(),得到一个值,且只包含第一个匹配到的值
-
注意:在使用search之前,必须对返回值进行判断是否存在,即是否在字符串中是否找到。
import re
ret = re.search('(www)\.(baidu|hao123)\.(com)', 'www.baidu.com')
print(ret.group(0))
print(ret.group(1))
print(ret.group(2))
print(ret.group(3))
输出:
www.baidu.com
www
baidu
com
#由此可以得出全部分组数据可以根据group(n)来得到,其中group(0)和group()等价会将整体输出。
-
补充:findall和search的分组区别
-
findall
会优先显示分组中的内容,如果需要结果的全部取到,必须取消分组优先,在分组前加?:
-
search
通过group()可以取到匹配的完整值,可以根据group(n)取出正则表达式中的各个分组的值。
-
3. re.match
-
作用:从头开始匹配,等同于正则表达式加上了一个
^
-
返回值:一个match对象,通过group()取值
-
应用场景:验证用户输入
import re s = 'abner' ret = re.match('ne',s) print(ret) 输出: None
- 由于match是从匹配字符串的头部开始匹配,而需要找的’ne‘没有在字符串首,故通过match没办法得到,而在此情况下通过search是可以把'ne'找到的
import re s = 'abner' ret = re.match('ab',s) print(ret) 输出: <_sre.SRE_Match object; span=(0, 2), match='ab'>
- 上述例子‘ab’位于匹配字符串首,所以可以找到匹配的字符
4. re.split
- 作用:通过正则表达式进行切割
- 返回值是一个列表,包含分割出来的元素,如果分组,则可以保留分割符
import re
s = 'Abner433Marry32Bob'
ret = re.split('\d+',s)
print(ret)
输出:
['Abner', 'Marry', 'Bob']
- 还可以和()分组连用,可以达到保留分割符的目的。
import re
s = 'Abner433Marry32Bob'
ret = re.split('(\d+)',s)
print(ret)
输出:
['Abner', '433', 'Marry', '32', 'Bob']
5. re.sub
- 作用:将满足正则匹配要求的字符替换成新的字符
- 参数:正则表达式,替换字符,匹配的字符串,[替换次数]
- 返回值:完成替换的新字符串
import re
s = 'Abner433Marry32Bob'
ret = re.sub('\d+','-',s)
print(ret)
输出:
Abner-Marry-Bob
-
补充:subn
- 作用:将满足正则匹配要求的字符替换成新的字符
- 参数:正则表达式,替换字符,匹配的字符串
- 返回值:完成替换的新字符串,替换次数组合成的元组
import re s = 'Abner433Marry32Bob' ret = re.subn('\d+','-',s) print(ret) 输出: ('Abner-Marry-Bob', 2)
6. re.compile
- 作用:编译正则规则,在多次需要使用同一个正则表达式时可以降低程序的冗余,对正则规则只进行一次编译,多次使用。
- 参数:正则规则
import re
com = re.complie('\d+')
print(com)
ret = com.search('abcd123')
ret2 = com.findall('abcd123ef456')
ret3 = com.finditer('qwer123t4')
#complie配合finditer使用可以降低程序的冗余并进行内存优化
print(ret,ret2,sep='\n')
for i in ret3:
print(i.group())
输出:
re.compile('\\d+')
<_sre.SRE_Match object; span=(4, 7), match='123'>
['123', '456']
123
4
7. re.finditer
- 作用:通过正则匹配,生成一个迭代器,可以逐个拿到满足要求的match对象。
- 应用场景:在处理大量信息时无法使用findall将数据全部加载进入内存,就必须通过finditer进行迭代取值。
- 参数:正则表达式,待匹配字符串。
import re
s = '2k1k3h4bjh34uhb21bu'
ret = re.finditer('\d+',s)
print(ret)
for i in ret:
print(i.group())
输出:
<callable_iterator object at 0x00000000021E96A0>
2
1
3
4
34
21
分组命名、分组约束
-
需求:匹配网页源码取出满足匹配正确标签的内容。
- 例子:
<h1>hello world</h1>
,前后的尖括号内的内容匹配
import re #程序一 s = '<h1>hello world</h1>' s1 = '<h1>hello world</h2>' pattern = r'<(?P<tag>.*?)>.*?</(?P=tag)>' ret = re.search(pattern,s) ret2 = re.search(pattern,s1) if ret: print(ret.group()) print(ret.group(1)) print(ret.group('tag')) 输出: <h1>hello world</h1> None h1 h1
import re #程序二 s = '<h1>hello world</h1>' pattern = r'<(.*?)>.*?</\1>' ret = re.search(pattern,s) if ret: print(ret.group()) print(ret.group(1)) 输出: <h1>hello world</h1> h1
- 由程序一可以看出正则中可以对一段规则进行分组和取名,并且在后面的规则中可以对分组的规则进行复用,达到一段规则,约束匹配字符串的多个部分的功能,使得匹配要求变成了不单单需要满足规则本身,并且使用分组的规则块约束的代码部分必须一致,方才可以完成匹配。
- 例子:
-
应用场景:爬取结构类似的网页源码中的信息
-
总结:
(?:正则规则) 表示取消优先显示的功能
(?P<组名>正则规则) 表示给匹配的字符串取组名
(?P=组名) 表示引用之前组的名字,引用部分匹配到的内容必须和之前那个组中的内容一模一样