匹配数字相关 '.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行 '^' 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","\nabc\neee",flags=re.MULTILINE) '$' 匹配字符结尾,或e.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()也可以 '*' 匹配*号前的字符0次或多次,re.findall("ab*","cabb3abcbbac") 结果为['abb', 'ab', 'a'],如果想控制一个组,可以加() '+' 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb'] '?' 匹配前一个字符1次或0次,常用来表示可有可无(出现1次或0次)的一个符号 '{m}' 匹配前一个字符m次 '{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb'] ,可以替代'*' '+' '?' 为:{0,} {1,} {0,1} '[]' 字符集,匹配[]中的字符(其中的字符是或的关系),其中的字符可以一一列出,也可以给出范围,元字符在其中失去意义,除非元字符前面加了反斜杠。有几种特殊的元字符依然有意义:表示范围的'-', 取反的'^',和'\'
1 >>> re.search('[a|b]','aabcd').group() # search方法在字符集匹配时只匹配一次2 'a'3 >>> re.search('[a|b]+','aabcd'). # '+' 匹配一次或多次字符集中的内容4 'aab'5 6 >>> re.findall('[a|b]','aabcd')7 ['a', 'a', 'b']8 >>>
'|' 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC' '(...)' 分组匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 结果 abcabca456c。 分组匹配时,findall只返回分组括号中的内容,返回到一个列表,分组作用:为了在已经匹配到的内容中再去提取过滤内容,有几个括号就提取几次 '\' 1.后面跟元字符去除特殊功能,2.跟普通字符实现特殊功能(\d:匹配数字),3.引用序号对应的字组所匹配的字符串
1 >>> re.search(r"(whisky)(\d+)com\2","whisky211985com211985").group()2 'whisky211985com211985'
'\A' 效果和^是一样的,只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的 '\Z' 匹配字符结尾,同$ '\d' 匹配单个 数字0-9 '\D' 匹配非数字 '\w' 匹配[A-Za-z0-9],注意:不能匹配空格、特殊字符等 '\W' 匹配非[A-Za-z0-9] '\s' 匹配空白字符、\t、\n、\r、\f、\v, re.search("\s+","ab\tc1\n3").group() 结果 '\t' ‘\b’ 匹配一个单词边界,即单词和空格(或特殊字符)间的位置,字符串开头结尾及空格回车等的位置,不会匹配空格符本身
1 >>> import re 2 >>> re.findall('abc\b','asdas abc ') 3 [] # 没有声明原生字符串或转义,所以结果为空 4 >>> re.findall('abc\\b','asdas abc ') 5 ['abc'] 6 >>> re.findall(r'abc\b','asdas abc ') 7 ['abc'] 8 >>> re.findall(r'abc\b','asdas abc*') 9 ['abc'] # 特殊字符作为单词边界10 11 >>> re.findall(r'i\b','i miss iou')12 ['i']13 >>> re.findall(r'i\b','imiss iou')14 []15 >>> re.findall(r'\bi',' imiss iou') # 以空格为单词边界16 ['i', 'i']17 >>> re.findall(r'\bi','imiss iou') #以字符串开头和空格为单词边界18 ['i', 'i']
'(?P...)' 分组匹配 re.search("(?P [0-9]{4})(?P [0-9]{2})(?P [0-9]{4})","371481199306143242").groupdict("city") 结果{'province': '3714', 'city': '81', 'birthday': '1993'} 注意:?P为固定语法格式
写法:(?P <组名> 正则表达式),输出(以字典的形式输出):{'组名':'正则匹配的结果'} 组名>
(*|+|?|{})? : *、+、?、{}后面加?使用非贪婪模式
1 正则表达式中 .*? 代表什么? 2 解答: 3 点代表的是任意字符。* 代表的是取 0 至 无限长度问号代表的是非贪婪模式。三个链接在一起是取尽量少的任意字符,一般不会这么单独写。 4 5 用法: 6 他大多用在:.*?a 7 8 解释: 9 就是取前面任意长度的字符,到底一个 a 出现,匹配如下q@wer_qwerqweraljlkjlkjlkj,10 11 得到:q@wer_qwerqwera 这部分,如果匹配不到后面的 a 字符,则匹配为空。
注意,re的若干方法: match方法是从字符串开头往后匹配(用的少) 例:
res = re.match('^Chen', 'Chenronghua123') 语法:pattern,string print(res) #输出:<_sre.SRE_Match object; span=(0, 4), match='Chen'>
#res = re.match('r.+', 'Chen123ronghua123') #匹配结果为空,match从字符串开头开始匹配 # res = re.search('r.+', 'Chen123ronghua123') #search 从整个文本中搜索 # print(res.group()) # 结果:ronghua
#如果匹配不到返回None,即res为None,res.group()会报错 常用方法如下: 1.search是从整个文本中搜索,匹配到一个就返回 2.findall是从整个文本中搜索,贪婪匹配,如果匹配到多个全部返回,所有结果都放在一个列表中,findall没有group方法
1 >>> re.findall('[a-z]','wwwa.d') 2 ['w', 'w', 'w', 'a', 'd'] 3 >>> re.findall('[0-9]','w0w3w4a.99d') 4 ['0', '3', '4', '9', '9'] 5 6 >>> re.findall('\d','w0w3w4a.99d') 7 ['0', '3', '4', '9', '9'] 8 >>> re.findall('\d', 'aa22ww2qq123qwe333') 9 ['2', '2', '2', '1', '2', '3', '3', '3', '3']10 >>> re.findall('\d\d', 'aa22ww2qq123qwe333')11 ['22', '12', '33']12 >>> re.findall('\d+', 'aa22ww2qq123qwe333')13 ['22', '2', '123', '333']14 15 >>> re.findall('\w','w0w3w4a.99d')16 ['w', '0', 'w', '3', 'w', '4', 'a', '9', '9', 'd']17 >>> re.findall('\s','w0w3 w4a.99d')18 [' ']
findall常和分组()搭配使用,分组后利用findall方法只返回分组括号中的数据,爬虫常用,也可取消这种功能,例:
1 >>> re.findall('www.(?:baidu|123).com','asd www.baidu.com') # 在括号的组中加上'?:'即可匹配全部内容,而非仅是分组中的内容2 ['www.baidu.com']
1 findall的分组、无分组: 2 >>> string = 'hello sky asd sky age sky qwe 19w' 3 >>> 4 >>> r = re.findall('a\w+', string) 5 >>> print(r) 6 ['asd', 'age'] 7 >>> r = re.findall('a(\w+)', string) #此时findall作用相当于groups,但groups把结果放入元组,findall则放入列表中 8 >>> print(r) 9 ['sd', 'ge']10 >>>11 >>> string = 'hello sky asd sky age sky qwe 19w'12 >>> r = re.findall('(a)(\w+)', string)13 >>> print(r)14 [('a', 'sd'), ('a', 'ge')] # string从前到后每次匹配到的字符串都放入元组,所有匹配到的内容都放入列表15 >>>16 >>> r = re.findall('(s)(\w+)(y)', string) # 相当于把groups中的内容放到一起17 >>> print(r)18 [('s', 'k', 'y'), ('s', 'k', 'y'), ('s', 'k', 'y')] #每个组中都有一个元素,共三个元素19 >>>20 >>> string = 'hello whisky asd whisky age whisky qwe 19w'21 >>> r = re.findall('(i)(\w+(k))(y)', string) #第一个元素是i 第二个:sk 第三个k 第四个:y,有几个括号就提取几次,取出的元素一次放入元组中,最后再把每次匹配的结果放入列表 ,另外,这里不支持re.findall('(i)(\w+(k))(?Py)', string)模版组名,用finditer可以支持22 >>> print(r)23 [('i', 'sk', 'k', 'y'), ('i', 'sk', 'k', 'y'), ('i', 'sk', 'k', 'y')]24 >>>25 >>> string = 'hello whisky asd whisky age whisky qwe 19w'26 >>> r = re.finditer('(i)(\w+(k))(?P y)', string) # 迭代方式可匹配组名27 >>> for i in r:28 ... print(i,i.group(),i.groups(),i.groupdict())29 ...30 <_sre.SRE_Match object; span=(8, 12), match='isky'> isky ('i', 'sk', 'k', 'y') { 'n1': 'y'}31 <_sre.SRE_Match object; span=(19, 23), match='isky'> isky ('i', 'sk', 'k', 'y'){ 'n1': 'y'}32 <_sre.SRE_Match object; span=(30, 34), match='isky'> isky ('i', 'sk', 'k', 'y'){ 'n1': 'y'}33 >>>34
1 findall:注意两个问题 2 1.findall无分组时匹配所有符合pattern的字符串 3 >>> n = re.findall('\d+\w\d+','a2b3c4d5') 4 >>> print(n) 5 ['2b3', '4d5'] # 结果不是3c4,可知findall方法是从前向后逐个匹配,匹配到结果2b3,则去掉2b3,再继续向后匹配 6 >>> 7 8 2.匹配到空的内容会返回 9 >>> n = re.findall('','a2b3c4d5')10 >>> print(n)11 ['', '', '', '', '', '', '', '', '']12 >>>13 14 3.指定分组次数时,因为只有一个括号(分组),所以只匹配最后一次内容15 >>> a = 'whisky'16 >>> n = re.findall('(\w)(\w)(\w)(\w)', a) #表示分4组,全部返回17 >>> print(n)18 [('w', 'h', 'i', 's')]19 >>> n = re.findall('(\w){4}', a) # 表示拿分组取字符串中匹配4次20 >>> print(n)21 ['s']22 >>>23 24 4. findall中的正则(如 *)会匹配到空,那么结果就会输出空,在开发时要避免正则表达式内容为空:25 >>> n = re.findall('(\dasd)*','1asd2asdp3asd98kif')26 >>> print(n)27 ['2asd', '', '3asd', '', '', '', '', '', ''] #*默认贪婪匹配,只有一个分组(括号),所以只返回2asd, *为空时,匹配结果为空,字符串最后默认有一个空字符,所以5个字符,匹配出6个空28 >>>29 >>> n = re.findall('(\dasd)+','1asd2asdp3asd4asd') #改为+,可以匹配想要的内容,不是输出空的结果30 >>> print(n)31 ['2asd', '4asd'] # 因为只有一个括号分组,所以只返回4asd,没有3asd
1 >>> import re 2 >>> p = re.compile(r'\d+') 3 >>> w = p.finditer('12 qwesdad44ers running, 22 ... 11 ...') 4 >>> for match in w: 5 ... match.group(),match.span() 6 ... 7 ('12', (0, 2)) 8 ('44', (10, 12)) 9 ('22', (25, 27))10 ('11', (32, 34))
小结:
findall其实就是一个一个的search,把search中的groups组合起来成为findall的内容,如果正则表达式中有一个分组,那么会去匹配组中的元素,放入列表;如果正则表达式中有多个组,会把组匹配到内容放到一个元组里面当作列表的一个元素,从前到后,重复此过程最后组成一个列表
3.match,group方法:
1 match 无分组: 2 >>> string = 'hello sky asd sky age sky qwe 19' 3 >>> r = re.match('h\w+',string) 4 >>> print(r.group()) # 获取匹配到的所有结果 5 hello 6 >>> print(r.groups()) # 只获取匹配到的分组结果 7 () 8 >>> print(r.groupdict()) # 获取匹配到的分组中所有执行了key的组 9 {}10 >>>11 12 match 有分组(分组作用:提取匹配成功的指定内容。先匹配成功全部正则,再把匹配成功的局部内容提取出来):13 >>> string = 'hello sky asd sky age sky qwe 19'14 >>> r = re.match('h(\w+)',string)15 >>> print(r.group())16 hello17 >>> print(r.groups())18 ('ello',)19 >>> print(r.groupdict())20 {}21 >>>22 指定分组名,放入字典23 >>> string = 'hello sky asd sky age sky qwe 19'24 >>> r = re.match("(?Ph)(?P \w+)",string)25 >>> print(r.group())26 hello27 >>> print(r.groups())28 ('h', 'ello')29 >>> print(r.groupdict())30 { 'n1': 'h', 'n2': 'ello'}31 >>>32 33 小结:34 对于group方法,无论是否有分组,将会匹配正则表达式中匹配到的所有结果35 对于groups方法,在有分组时,将匹配分组括号中(可以有多个分组)的内容,无分组则结果为空36 对于groupdict方法,在有分组时,将匹配分组中所有执行了key的组。无分组则结果为空37 写法:(?P <组名> 正则表达式),输出(以字典的形式输出):{'组名':'正则匹配的结果'},常用语jangle中的路由系统38 无分组匹配时,上述三个方法,只有group有意义 组名>
4.split分隔方法
1 # split 2 # 无分组 3 string = 'hello sky ald qwe lge zxc 27' 4 n = re.split('s\w+', string) 5 print(n) 6 # 输出 ['hello ', ' ald qwe lge zxc 27'] 7 8 # 有分组,split方法输出的结果如果用了分组,只保留分组中的内容 9 n = re.split('(s\w+)', string)10 print(n)11 # 输出 ['hello ', 'sky', ' ald qwe lge zxc 27']12 n = re.split('s(\w+)', string)13 print(n)14 # 输出 ['hello ', 'ky', ' ald qwe lge zxc 27']15 16 inpp = '1-2*((60-30 +(-40-5)*(9-2*5/3 + 7/3*99/4*2998 +10 * 568/14)) - (-4*3)/(16-3*2))'17 n = re.split('\(([^()]+)\)', inpp) # 分割不含括号的最里层表达式,并去掉括号(表达式中转义符去匹配括号,里边括号作用split结果只取分组中的内容)18 print(n)19 # 输出 ['1-2*((60-30 +', '-40-5', '*', '9-2*5/3 + 7/3*99/4*2998 +10 * 568/14', ') - ', '-4*3', '/', '16-3*2', ')']
4.sub替换方法
1 # sub不涉及分组 2 string = '1qqas2uuii234ashjd888kkasd333' 3 n = re.sub('\d+', '----', string) 4 print(n) 5 new_str, count = re.subn('\d+', 'KKK', string) 6 print(new_str, count) 7 8 #输出: 9 ----qqas----uuii----ashjd----kkasd----10 ('KKKqqasKKKuuiiKKKashjdKKKkkasdKKK', 5)
5.start() 返回匹配开始的位置 end() 返回匹配结束的位置 6.span() 返回一个元组包含匹配(开始,结束)的位置 几个匹配模式: 1.re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同) 2.re.M(MULTILINE): 多行模式,改变'^'和'$'的行为(参见上图) [用得很少] 3.re.S(DOTALL): 使.匹配包括换行在内的所有字符,点任意匹配模式,改变'.'的行为
贪婪和非贪婪:
1 >>> re.search(r'a(\d+)','a3333333345b').group() 2 'a3333333345' 3 >>> re.search(r'a(\d+?)','a3333333345b').group() 4 'a3' 5 >>> re.search(r'a(\d*)','a3333333345b').group() 6 'a3333333345' 7 >>> re.search(r'a(\d*?)','a3333333345b').group() 8 'a' 9 10 如果分组括号两侧都有限制条件即:a()b,则分组中的?非贪婪匹配不再起作用11 >>> re.findall(r'a(\d+)b','a23b')12 ['23']13 >>> re.findall(r'a(\d+?)b','a23b')14 ['23']15 >>>
原生字符串:(字符串前面加r,即声明使用原生字符串):
正则表达式中的r:(使用r前缀后,匹配字符串中所有字符都不转义,特殊字符不再有转义功能,只代表一个字符串)
r意思就是raw data,也就是原始数据,不用转义的。比如在一个字符串里面包含斜杠和一个字母n,"\n"就错了,这里的斜杠和n的组合在python中表示一个换行,必须"\\n",也就是用反斜杠来转义反斜杠。但是用r后面接字符串就没这个问题了,r"\n"中的\n就是这两个个字母本身了。
1 >>> import re 2 >>> re.search(r"\\","ba\cabc").group() 3 '\\' 4 >>> re.search("\\","ba\cabc").group() 5 Traceback (most recent call last): 6 File "", line 1, in 7 >>> re.search(r"\\","ba\cabc").group() 8 '\\' 9 >>> re.findall("\\","ba\cabc")10 Traceback (most recent call last):11 File " ", line 1, in 12 >>> re.findall(r"\\","ba\cabc")13 ['\\']
注:
在python中,\n是换行,\r是回车,\b是退格
1 python中关于正则内的\b,为什么使用\b时需要用r'\b',但是\w则不需要? 2 解答: 3 因为\b 有两种解释,而\w 只有一种。 4 5 \b的两种解释是: 6 7 1.'\b', 如果前面不加r, 那么解释器认为是转义字符“退格键backspace”; 8 2.r'\b', 如果前面加r, 那么解释器不会进行转义,\b 解释为正则表达式模式中的字符串边界。 9 10 而相比于\b, \w 只有第二种解释,并没有对应的转义字符,所以不加r, 也不会出错。
1 >>> import re2 >>> re.findall('abc\b','asdas abc ') # 由于\b在python中表示退格,所以匹配不到内容3 []4 >>> re.findall('abc\\b','asdas abc ') # 转义5 ['abc']6 >>> re.findall(r'abc\b','asdas abc ') # 声明原生字符串,\b此时表示单词边界7 ['abc']8 >>>
split方法: res = re.split('[0-9]+', 'abc12de3f45GH') print(res) 输出:['abc', 'de', 'f', 'GH'] sub方法: res = re.sub('[0-9]+', '|', 'abc12de3f45GH', count=2) print(res) 输出:abc|de|f45GH 1.re.I(re.IGNORECASE): 忽略大小写 res = re.search('[a-z]+', 'abcGH', flags=re.I) print(res.group()) 输出:abcGH 2.M(MULTILINE): 多行模式,改变'^'和'$'的行为 res = re.search(r"^a", "\nabc\neee", flags=re.M) print(res.group()) 输出:a 3.S(DOTALL): 点任意匹配模式,改变'.'的行为 res = re.search(".+", "\nabc\neee", flags=re.S) print(res.group()) 输出:a 举例: '.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行 res = re.match('.+', 'Chen123ronghua123') print(res.group()) 输出: Chen123ronghua123 '$' 匹配字符结尾,或e.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()也可以 res = re.match('r.+', 'Chen123ronghua123') #匹配结果为空,match从字符串开头开始匹配 res = re.search('r.+', 'Chen123ronghua123') #search 从整个文本中搜索 print(res.group()) 结果:ronghua '+' 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb'] res = re.search('r[a-z]+a', 'Chen123ronghua123') #匹配ronghua print(res.group()) 结果:ronghua res = re.search('#.+#', '1123#hello#') print(res.group()) 结果:#hello# '?' 匹配前一个字符1次或0次 res0 = re.search('aal?', 'aalex') res1 = re.search('aal?', 'aaex') print(res0.group()) print(res1.group()) 输出 aal aa '{m}' 匹配前一个字符m次 res = re.search('[0-9]{3}', 'aa1xe2pp345lex') #匹配前面的数字三次 print(res.group()) '{n,m}' 匹配前一个字符n到m次 res = re.search('[0-9]{1,3}', 'aa1xe2pp345lex') #匹配前面的数字1到3次 print(res.group()) 输出 1 findall 贪婪匹配 res = re.findall('[0-9]{1,3}', 'aa1xe2pp345lex') #findall,贪婪匹配,匹配前面的数字1到3次 print(res) 输出['1', '2', '345'] #以列表的形式返回 '|' 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC' res = re.search('abc|ABC', 'ABCBabcCD') print(res.group()) 输出 ABC res = re.findall('abc|ABC', 'ABCBabcCD') print(res) 输出 ['ABC', 'abc'] '(...)' 分组匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 结果 abcabca456c res = re.search('(abc){2}', 'alexabcabc') print(res.group()) 输出 abcabc res = re.search('(abc){2}(\|\|=){2}', 'alexabcabc||=||=') 匹配||= 两次,注意需要转义 print(res.group()) 输出:abcabc||=||= '\D' 匹配非数字 res = re.search('\D+', '123$- a') print(res.group()) 输出:$- a '\w' 匹配[A-Za-z0-9] 除了特殊字符都匹配 res = re.search('\w+', '123$- a') print(res.group()) 输出:123 '\W' 匹配非[A-Za-z0-9] 只匹配特殊字符 res = re.search('\W+', '123$- ...a') print(res.group()) 输出:$- ... '\s' 匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t' res = re.findall('\s', '123$- \r\n\t...a') print(res) 输出:[' ', '\r', '\n', '\t'] >>> re.search('\s+', '123$- \r\n') <_sre.SRE_Match object; span=(5, 9), match=' \t\r\n'> '\A' 效果和^是一样的,只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的 '\Z' 匹配字符结尾,同$ '\d' 匹配数字0-9 例: res = re.search('\A[0-9]+[a-z]\Z', '123a') print(res.group()) 输出:123a * : 0个至多个 + :1个至多个 res = re.match('^Chen\d+', 'Chen123ronghua123') print(res) print(res.group()) #查看匹配到的对象 输出:<_sre.SRE_Match object; span=(0, 7), match='Chen123'> Chen123 '(?P...)' 分组匹配 res = re.search("(?P [0-9]{4})(?P [0-9]{2})(?P [0-9]{4})","371481199306143242").groupdict("city") print(res) 结果{'province': '3714', 'city': '81', 'birthday': '1993'}