# 正则表达式
应用场景:1.检测某一段字符串是否符合规则
2.从某一段文字中找到符合规则的内容
字符组[ ] :在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[ ]表示
- 数字: [0-9]
- 字母:小写[a-z]、大写[A-Z ]、大小写
[A-Za-z]
、数字和大小写字母[A-Z0-9a-z] - 注意:在字符组中 - 是有特殊意义的,如果我们想取消这个横杠的特殊意义,需要使用时要使用\转义
字符组之外的其他带有特殊规则的元字符:
正则模块 正则表达式 元字符 : . 匹配除了回车以外的所有字符 \w 数字字母下划线 \d 数字 \n \s \t 回车 空格 和 tab ^ 必须出现在一个正则表达式的最开始,匹配开头 $ 必须出现在一个正则表达式的最后,匹配结尾 | 或 a|b 要么取左边的要么取右边的 ()|() 分组中的或 一定是长的在前面 短的在后面 [] 在同一个位置上可能出现的所有字符都放在组里 [^] 在同一个位置上不能出现的所有字符都放在组里 () 对于一整个组做量词约束 ; python 分组优先 量词 : * 0次或多次 + 1次或多次 ? 0次或1次 {} 具体的 {n},{n,m},{n,} 问号的用法 惰性匹配 : 量词+? 表示使用惰性匹配 分组优先 findall split ;取消分组优先 (?:。。) 分组命名 (?P<name>...)
元字符:匹配的是一个字符的内容 量词:表示的是匹配的次数
贪婪匹配: 默认的 尽可能多的匹配 如:a.*以a开头的任意一个字符都可以被匹配出来
惰性匹配:也叫做非贪婪匹配 如: a.*?x 从a开始匹配,匹配任意长度的字符没遇到一个x就会停下来,而贪婪匹配就会一直匹配到最后一个x
abc # 测试数据 bc # 测试数据 a*.c # 正则规则1,表示a可出现可不出现 --> #结果:abc 、 bc a.*c #正则规则2 --> #结果:abc
# re模块
import re
# 根据正则规则从一段内容中查找结果
# findall返回所有满足匹配条件的结果,放在一个列表中
ret = re.findall('\d+','alex223yuan')
print(ret) #['223']
#search 只匹配第一个满足条件的结果对象,通过对象的group()方法取出
#如果没有匹配到,会返回None,再group()会报错
ret = re.search('\d+','alex123yuan234') # <re.Match object; span=(4, 7), match='123'>
print(ret.group()) # 123
# match 用法和search一样,但是只会从开头匹配,类似于从头到尾的match,没有返回None,再group()报错
ret = re.match('\d+','123asf') #<re.Match object; span=(0, 3), match='123'>
print(ret.group()) # 123
#match可以被替代么? 可以
#re.search('^\d+','alexyuan') == re.match('\d+','123alex')
# 将数字替换成'sb'
ret = re.sub('\d+','sb','alex84wusir73') # 默认替换所有 返回alexsbwusirsb
ret = re.sub('\d+','sb','alex84wusir73',1) # 写了1表示替换一次 返回alexsbwusir73
ret = re.subn('\d', 'H', 'eva3egon4yuan4') #将数字替换成'H',返回元组(替换的结果,替换了多少次)
ret = re.subn('\d+','sb','alex84wusir73')
print(ret) # ('alexsbwusirsb', 2) 结果还会匹配的次数
obj = re.compile('\d{3}') #将正则表达式编译成为一个正则表达式对象(预编译),规则要匹配的是3个数字
ret = obj.search('abc123eeee') #正则表达式对象调用search,参数为待匹配的字符串
print(ret.group()) #结果 : 123
# 节省空间
ret = re.finditer('\d', 'ds3sy4784a') #finditer返回一个存放匹配结果的迭代器
print(ret) # <callable_iterator object at 0x10195f940>
print(next(ret).group()) #查看第一个结果
print(next(ret).group()) #查看第二个结果
print([i.group() for i in ret]) #查看剩余的左右结果
ret = re.split('\d+','alex84wusir73yuan') # 以匹配对象分隔,返回一个列表
print(ret) #['alex', 'wusir', 'yuan']
ret = re.split('(\d+)','alex84wusir73yuan') # 以匹配对象分隔,返回一个列表,加括号时,会取出数字
print(ret) #['alex', '84', 'wusir', '73', 'yuan']
- findall的优先级查询:
import re
# 只有findall会优先选择括号里面的,需要用?:取消优先,可以通过这个特性匹配而不取那个结果
ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
print(ret) # ['oldboy'] 这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可
ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')
print(ret) # ['www.oldboy.com'] 使用?:来取消分组优先
ret=re.split("\d+","eva3egon4yuan")
print(ret) #结果 : ['eva', 'egon', 'yuan']
ret=re.split("(\d+)","eva3egon4yuan")
print(ret) #结果 : ['eva', '3', 'egon', '4', 'yuan']
#在匹配部分加上()之后所切出的结果是不同的,
#没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项,
#这个在某些需要保留匹配部分的使用过程是非常重要的。
# 注意点:
ret = re.findall('\d{4}(?P<ta n\d{1,2}(?P=tag)\d{1,2}','2011-10-22')
ret1 = re.findall('\d{4}(?:[\.\-\+@])\d{1,2}(?:[\.\-\+@])\d{1,2}','2011-10-22')
print(ret) # ['-']
print(ret1) # ['2011-10-22']
# findall里面分组命名与取消分组优先 不能共存
# 用search可以解决分组命名与取消分组优先问题
ret3 = re.search('(?P<year>\d{4})(?P<tag>[\.\-\+@])\d{1,2}(?P=tag)\d{1,2}','2011-10-22')
print(ret3.group()) # 2011-10-22
print(ret3.group('year')) # 2011
- 匹配最里层括号: (
[^()]+
) #表示括号内都是非括号的字符组成 - 匹配在运算表达式字符串中带有负号的数(这个必须用分组括住,不然显示不正确):(-?\d+(?:.\d+)?)
- 匹配乘除: \d+(.\d+)?[*/]-?\d+(.\d+)?
- 匹配嘴里层没有括号:
count ='1-2*((60-30+(-9-2*5/3+7/3*99/4*2998+10*568/14)*(-40/5))-(-4*3)/(16-3*2))' ret = re.search(r'\([^()]+\)',count)
- 分别取出1年的12个月(^(0?[1-9]|1[0-2])$)
注意平时要用到的参数:
flags有很多可选值:
re.I(IGNORECASE)忽略大小写,括号内是完整的写法n
re.M(MULTILINE)多行模式,改变^和$的行为
re.S(DOTALL)点可以匹配任意字符,包括换行符 ,常用
re.L(LOCALE)做本地化识别的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境,不推荐使用
re.U(UNICODE) 使用\w \W \s \S \d \D使用取决于unicode定义的字符属性。在python3中默认使用该flag
re.X(VERBOSE)冗长模式,该模式下pattern字符串可以是多行的,忽略空白字符,并可以添加注释
使用案列;
import re
a = """sdfkhellolsdlfsdfiooefo:
877898989worldafdsf"""
b = re.findall('hello(.*?)world',a)
c = re.findall('hello(.*?)world',a,re.S)
print ('b is ' , b)
# 输出结果:
# b is []
# c is ['lsdlfsdfiooefo:\n877898989']
**分组()**的注意事项:
1. findall #会优先显示分组中的内容 取消分组优先 (?:正则) 2. s = '123alex234wusir' ret = re.search('(\d+).*?(\d+)',s) #group(2) 结果 234 print(ret.group(1)) # 123 print(ret.group(2)) ret = re.search('(\d+)\w+(\d+)\w+(\w)',s) #优先返回括号里面的 print(ret.group(1)) # 123 print(ret.group(2)) # group(2) 结果 4 贪婪匹配 print(ret.group(3)) # r 贪婪匹配 3. #分组命名 (?P<NAME>正则) 在爬虫时常用: 只适用于match和search,因为findall是列表 ret = re.search('\d+(?P<name>.*?)\d+',s) print(ret.group('name')) # alex # 特殊用法,双端匹配 ret = re.search('<(?P<tag>\w+)>.*</(?P=tag)>','<h1>wahaha</h2></h1></h3>') print(ret.group()) # <h1>wahaha</h2></h1> print(ret.group('tag')) # h1 4. #引用分组 法1:(?P=name) 法二:\1 \2 适用于search和findall pattern = r'<(\w+)>(\w+)</(\1)>' #\1 直接表示了第一个()里的内容, 这个没有tab,没有组名,直接用数字表达了,但是需要在字符 串前加上一个r, 对\1转义 ret = re.findall(pattern,'<a>wahaha</a>') print(ret) #[('a', 'wahaha', 'a')] s = '<a>wahaha</a>' pattern = r'<(\w+)>(\w+)</(\1)>' #\1 直接表示了第一个()里的内容, 这个没有tab,没有组名,直接用数字表达了,但是需要在字符串前加上一个r, 对\1转义 ret = re.search(pattern,s) print(ret.group()) #<a>wahaha</a> print(ret.group(2)) # wahaha #注意 import re ret=re.search('\d+?7','123455677ewr7').group() #12345567 注意这里?号不能单独出现在最前面,要么加'.' ret1=re.search('.?7','123455677ewr7').group() #123455677 ret3=re.search('\w*7','123455677ewr7').group() #67 ret2=re.search('\d+7','123455677ewr7').group() #123455677ewr7 print(ret,ret2,ret1,ret3)
正则的应用
#爬虫应用 import re import json from urllib.request import urlopen def getPage(url): response = urlopen(url) return response.read().decode('utf-8') def parsePage(s): com = re.compile( '<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>' '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>', re.S) ret = com.finditer(s) for i in ret: yield { "id": i.group("id"), "title": i.group("title"), "rating_num": i.group("rating_num"), "comment_num": i.group("comment_num"), } def main(num): url = 'https://movie.douban.com/top250?start=%s&filter=' % num response_html = getPage(url) ret = parsePage(response_html) print(ret) f = open("move_info7", "a", encoding="utf8") for obj in ret: print(obj) data = str(obj) f.write(data + "\n") f.close() count = 0 for i in range(10): main(count) count += 25