随着大数据乃至人工智能的迅猛发展,数据变得越来越重要,甚至已成为很多企业赖以生存的根基。而想要获取数据,爬虫是必备工具之一。而正则表达式在爬虫抓取内容中扮演中重要角色,要想成为一个合作的爬虫工程师,就必须熟练的掌握正则表达式的知识。
正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。re模块使 Python 语言拥有全部的正则表达式功能。本文主要是记录一下本人学习Python的正则表达式re模块常用处理函数的笔记,言归正传,下面进入正文。
re.match函数
re.match(pattern, string, flags=0),尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
1.pattern 匹配的正则表达式
2.string 要匹配的字符串
3.flags标志位,用于控制正则表达式的匹配方式,修饰符
其中修饰符如下:
如下,匹配到起始位置的:
#!/usr/bin/python
import re
line = "Cats are smarter than dogs"
matchObj = re.match( r'(.*) are (?P<value>.*?) .*', line, re.M|re.I)
print(matchObj)
打印结果为
<_sre.SRE_Match object; span=(0, 26), match='Cats are smarter than dogs'>
如上,返回一个re.MatchObject对象,该对象有如下的方法:
1.group(num=0),0可以不传入,返回字符串,group(num=1)返回正则第一分组匹配的字符串,group(name)返回匹配指定分组的字符串
2.groups(),以元组返回规则里面所有括号里匹配的字符串
3.start(num=0),0可以不传入,返回匹配的起始位置,start(name)返回匹配指定分组的字符串起始位置
4.end(num=0),0可以不传入,返回匹配的结束位置,end(name)返回匹配指定分组的字符串结束位置
5.span(num=0),0可以不传入,返回匹配的字符串的(start, end),span(name)返回匹配指定分组的字符串的(start, end)
如下,代码接上面:
if matchObj:
print("matchObj.group('value') : ", matchObj.group('value'))
print("matchObj.group(1) : ", matchObj.group(1))
print("matchObj.group(2) : ", matchObj.group(2))
print("matchObj.groups() : ",matchObj.groups())
print("matchObj.start(2) : ",matchObj.start(2))
print("matchObj.end(2): ",matchObj.end(2))
print("matchObj.span(): ",matchObj.span())
else:
print("No match!!")
打印结果为
matchObj.group('value') : smarter #获取正则表达式指定分组(?P<value>.*?)匹配的字符串
matchObj.group(1) : Cats #获取正则表达式第一分组(.*)匹配匹配的字符串
matchObj.group(2) : smarter #获取正则表达式第二分组(.*)匹配匹配的字符串
matchObj.groups() :('Cats', 'smarter') #获取全部分组匹配的字符串,以元组形式返回
matchObj.start(2) :9 #返回正则表达式第二分组(.*)的起始位置
matchObj.end(2): 16 #返回正则表达式第二分组(.*)的结束位置
matchObj.span():(0, 26) #返回匹配到字符串的起始和结束位置
如果,没有匹配到起始位置,则会返回None,如下:
print(re.match('com', 'www.baidu.com')) #没有匹配起始位置,打印结果为None
re.search函数
re.search(pattern, string, flags=0),扫描整个字符串并返回第一个成功的匹配;
1.pattern 匹配的正则表达式
2.string 要匹配的字符串
3.flags标志位,用于控制正则表达式的匹配方式,修饰符 (见图image.png)
如下:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import re
print(re.search('www', 'www.runoobwww.com').span()) # 在起始位置匹配
print(re.search('com', 'www.runoobcom.com').span()) # 不在起始位置匹配
打印结果如下,可以匹配字符串的任意位置,匹配到后直接返回结果,不继续进行匹配:
(0, 3)
(10, 13)
该方法会返回一个对象格式的数据<class '_sre.SRE_Match'>,具有如下的方法:
1.group(num=0),0可以不传入,返回字符串,group(num=1)返回正则第一分组匹配的字符串,group(name)返回匹配指定分组的字符串
2.groups(),以元组返回规则里面所有括号里匹配的字符串
3.start(num=0),0可以不传入,返回匹配的起始位置,start(name)返回匹配指定分组的字符串起始位置
4.end(num=0),0可以不传入,返回匹配的结束位置,end(name)返回匹配指定分组的字符串结束位置
5.span(num=0),0可以不传入,返回匹配的字符串的(start, end),span(name)返回匹配指定分组的字符串的(start, end)
具体的用法如下:
#!/usr/bin/python
import re
line = "Cats are smarter than dogs";
searchObj = re.search( r'(.*) are (.*?) .*', line, re.M|re.I)
if searchObj:
print("searchObj.groups() : ", searchObj.groups())
print("searchObj.group(1) : ", searchObj.group(1))
print("searchObj.group(2) : ", searchObj.group(2))
print("searchObj.start(2) :",searchObj.start(2))
print("searchObj.end(2): ", searchObj.end(2))
print("searchObj.span(): ", searchObj.span())
else:
print("Nothing found!!")
打印结果为
searchObj.groups() : ('Cats', 'smarter') #获取全部分组匹配的字符串,以元组形式返回
searchObj.group(1) : Cats #获取正则表达式第一分组(.*)匹配匹配的字符串
searchObj.group(2) : smarter #获取正则表达式第二分组(.*)匹配匹配的字符串
searchObj.start(2) : 9 #返回正则表达式第二分组(.*)的起始位置
searchObj.end(2): 16 #返回正则表达式第二分组(.*)的结束位置
searchObj.span(): (0, 26) #返回匹配到字符串的起始和结束位置
如果,没有匹配任何字符串,则会返回None,如下:
print(re.match('c1om', 'www.baidu.com')) #没有匹配起始位置,打印结果为None
re.sub函数
re.sub(pattern, repl, string, count=0, flags=0)方法用来替换字符串;
1.pattern 匹配的正则表达式
2.repl 替换的字符串,也可以为一个函数
3.string 要匹配的字符串
4.count 模式匹配后替换的最大次数,0的情况下是全部替换
5.flags 标志位,用于控制正则表达式的匹配方式,修饰符 (见图image.png)
如下:
#!/usr/bin/python
import re
s = '2004-959-559 # 这是一个国外电话号码'
phone = re.sub('#\D+', '', s ) #使用空字符替换后面的'# 这是一个国外电话号码'
print(phone)
print(re.sub('\-', '', phone, 1)) # count =1,替换一次匹配到的字符串
打印结果如下:
2004-959-559 #替换#+后面的字符串后,得到前面的电话号码
2004959-559 #替换一次'-'后的结果
repl为函数的情况下,代码如下
#!/usr/bin/python
import re
def double(matched):
value = int(matched.group('name')) #获取匹配组name的字符串,对其进行取int
return str(value*2)
s = 'A23G4HFD567'
print(re.sub('(?P<name>\d+)', double, s))
打印如下:
A46G8HFD1134 #匹配出数字字符,对其int后进行*2操作后,替换原来的数字字符
这里穿插说下,正则表达式分组命名的问题,格式为(?P<name>...),如上面(?P<name>\d+),就是将这个分组命名为name,这个就可以通过调用group('name')方法获取这个分组匹配的字符串。
re.compile 函数
re.compile(pattern[, flags]),用于编译正则表达式,生成一个正则表达式( Pattern )对象;
1.pattern 匹配的正则表达式
2.flags标志位,用于控制正则表达式的匹配方式,修饰符 (见图image.png)
如下:
#!/usr/bin/python
import re
pattern = re.compile(r'(.*) are (?P<value>.*?) .*')
如上所示,返回re.RegexObject 对象,具有如下的常用方法:
1.findall(string[, pos[, endpos]]),参数如下:
1.string 待匹配的字符串。
2.pos 可选参数,指定字符串的起始位置,默认为 0
3.endpos 可选参数,指定字符串的结束位置,默认为字符串的长度
findall()方法,返回字符串所有非重叠匹配的列表,如果没有匹配到,则返回空列表,如下:
#!/usr/bin/python
import re
pattern = re.compile(r'\d+') #查找数字
result1 = pattern.findall('runoob 123 google 456')
result2 = pattern.findall('run88oob123google456', 0, 10)
result3 = pattern.findall('runoobgoogle')
print(result1)
print(result2)
打印结果如下:
['123', '456'] #找出这个字符串中所有的数字字符
['88', '12'] #字符串索引0到10之间,查找所有的数字字符
[] #没有匹配任何内容,返回空的列表
另外,对于findall()方法,如果匹配的正则表达式中有分组的话,则以列表的形式返回分组匹配到字符串,如果有多个分组,则返回元组列表,如下只有一个分组的情况:
#!/usr/bin/python
import re
s = "Cats are smarter than dogs"
pattern = re.compile(r'(.*) are .*', re.M|re.I)
print(pattern.findall(s))
打印结果如下:
['Cats'] # 找出分组(.*)匹配的字符串
有多个分组的情况下,如下:
#!/usr/bin/python
import re
s = "Cats are smarter than dogs"
pattern = re.compile(r'(.*) are (.*?) .*', re.M|re.I)
print(pattern.findall(s))
打印结果如下:
[('Cats', 'smarter')] #返回一个list,元素为元组
这个有个问题,如果pattern = re.compile(r'(.) are (.) .*', re.M|re.I),去掉中间分组那个?,打印出来的结果跟上面不一样,如下
#!/usr/bin/python
import re
s = "Cats are smarter than dogs"
pattern = re.compile(r'(.*) are (.*) .*', re.M|re.I)
print(pattern.findall(s))
打印结果如下:
[('Cats', 'smarter than')]
这到底是为什么呢,这个问题困扰了我很久,原来在正则表达式中*是贪婪匹配,要尽可能多的匹配,所以第二分组贪婪模式下,匹配的字符串为smarter than,上面的处理是在正则表达式外面加?,使用贪婪模式转换成非贪婪模式。所以使用正则表达式进行匹配字符串的时候,尽量使用非贪婪模式,比如用".*?"代替".*",为了防止贪婪模式的情况下,导致获取的字符串缺失。但是如果".*?"出现结尾的话,就会匹配不到任何的内容,因为它会匹配尽可能少的字符,这是需要使用".*".
另外,findall()方法匹配的字符串只是正则表达式中分组匹配的内容,有的时候跟我们想要获取的字符串不一致,那么我们想获取全部正则匹配的内容,该这么办呢,下面举例说明一下这个问题:
#!/usr/bin/python
import re
s = "Cats are smarter than dogs"
pattern = re.compile(r'(?:.*) are (?:.*) .*', re.M|re.I) #在分组正则表达式前面(括号里)加'?:',使得捕获组变成非捕获组
print(pattern.findall(s))
打印结果如下:
['Cats are smarter than dogs'] #设置成非捕获组后,匹配出全部的内容。
2.match(string[, pos[, endpos]])参数如下:
1.string 待匹配的字符串。
2.pos 可选参数,指定字符串的起始位置,默认为 0
3.endpos 可选参数,指定字符串的结束位置,默认为字符串的长度
如下:
#!/usr/bin/python
import re
pattern = re.compile(r'\d+')
s = pattern.match('one12twothree34four', 3, 10) #传入pos和endpos参数,从pos位置进行匹配,endpos位置结束
print(s)
打印结果如下:
<_sre.SRE_Match object; span=(3, 5), match='12'> #指定从字符1进行匹配,能够匹配到数据字符串。
该方法返回MatchObject 对象,有group(),groups(),start(),end()和span()方法,具体的用法见上面。
re.finditer 函数
re.finditer(pattern, string, flags=0),与findall类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回,每个迭代都是一个MatchObject 对象;
1.pattern 匹配的正则表达式
2.string 要被匹配的字符串
3.flags 标志位,用于控制正则表达式的匹配方式,修饰符 (见图image.png)
如下:
#!/usr/bin/python
import re
s = '2004-959-559 # 这是一个国外电话号码'
pattern = re.compile(r'\d+')
iter = pattern.finditer(s) #返回一个迭代器
for i in iter:
print(i.group()) #调用group()方法获取匹配的内容
打印结果如下:
#打印出匹配的全部数字字符
2004
959
559
re.split 函数
re.split(pattern, string[, maxsplit=0, flag=0]),按照能够匹配的子串将字符串分割后返回列表;
1.pattern 匹配的正则表达式
2.string 要被匹配的字符串
3.maxsplit 分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数。
4.flags 标志位,用于控制正则表达式的匹配方式,修饰符 (见图image.png)
如下:
#!/usr/bin/python
import re
s = 'runoob, runoob, runoob'
print(re.split('\W+', s))
print(re.split('(\W+)', s))
print(re.split('\W+', s), 1) #传入maxsplit=1,分隔一次
打印结果如下:
['runoob', 'runoob', 'runoob']
['runoob', ', ', 'runoob', ', ', 'runoob']
['runoob', 'runoob, runoob'] #传入maxsplit=1,分隔一次
这里有个疑问,第一个print打印的内容,连','都做了分隔项了,跟上面的区别是,对正常表达式做了捕获组,希望亲爱的您能够帮忙我解答一下,谢谢
断断续续的,终于这个总结给写完了,对我自己来说,又巩固了一下正则方面的知识点,加深了印象,也希望能够给亲爱的您带来帮助。
本篇如果对您有帮助,请点个👍,谢谢。