# 正则表达式

  • 应用场景:1.检测某一段字符串是否符合规则

    ​ 2.从某一段文字中找到符合规则的内容

  • 字符组[ ] :在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[ ]表示

    1. 数字: [0-9]
    2. 字母:小写[a-z]、大写[A-Z ]、大小写[A-Za-z]、数字和大小写字母[A-Z0-9a-z]
    3. 注意:在字符组中 - 是有特殊意义的,如果我们想取消这个横杠的特殊意义,需要使用时要使用\转义
  • 字符组之外的其他带有特殊规则的元字符:

    正则模块
    正则表达式
    元字符 :
        .   匹配除了回车以外的所有字符
       \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

  1. 匹配最里层括号: ([^()]+ ) #表示括号内都是非括号的字符组成
  2. 匹配在运算表达式字符串中带有负号的数(这个必须用分组括住,不然显示不正确):(-?\d+(?:.\d+)?)
  3. 匹配乘除: \d+(.\d+)?[*/]-?\d+(.\d+)?
  4. 匹配嘴里层没有括号: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)
  5. 分别取出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