正则表达式&re模块 小感~

正则表达式&re模块

正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。
Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。
re 模块使 Python 语言拥有全部的正则表达式功能。
compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。
re 模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。

正则表达式

  • 正则:是一种规则,匹配字符串的规则
    • 正则的规则:
      • 本身是哪一个字符就匹配字符串中的哪个字符
      • 字符组[字符1字符2],一个人 字符组就代表匹配一个字符,只要这个字符出现在字符组里,那么就说明这个字符能匹配上(字符组中还可以适用范围,所有范围都必须遵循ascii码从小到大来制定[0-9] [a - z] [ A - Z ])

元字符:

  • [0-9] d 表示多有数字
  • w 表示字母.数字.下划线
  • s 表示空格.制表符.换行符
  • 表示制表符
  • 表示换行符
  • D 表示非数字
  • W表示除数字字母下划线外的所有字符
  • S 表示非空白
  • . 表示除了换行符之外的所有字符
  • []字符组:只要在括号中的所有字符都是符合规则的字符
  • [^]表示在括号中的所有字符都不符合规则
  • ^ 表示一个字符的开始
  • $ 表示一个字符的结束
  • | 表示或,如果两个规则有重叠,总是唱的在前面短的在后面
  • () 表示分组,给一部分正规则规定为一组,|这个符号的作用域就可以缩小了
  •  表示两端是什么

量词:

  • {n} 表示出现n次
  • {n,}表示至少出现n次
  • {n,m}表示至少出现n次,最多出现m次
  • ? 表示匹配0次或者1次 表示可有可无 但是有只能有一个比如小数点
  • +表示匹配1次或多次
  • *表示匹配0次或多次 表示可有可无但是有可以有多个比如肖淑娴后n位
  • .*?x表示匹配任意的内容任意多次,一旦遇到x就停止
  • a??表示匹配0次a
匹配0次
    # 整数 d+
    # 小数 d+.d+
    # 整数或小数 : d+.?d*
    # 分组的作用 : d+(.d+)?
贪婪匹配

​ 在量词范围允许的情况下,尽量多的匹配内容
​ .*x 表示匹配任意字符 任意多次数 遇到最后一个x才停下来

非贪婪(惰性)匹配

​ .*?x 表示匹配任意字符 任意多次数 但是一旦遇到x就停下来 -- >就是量词后边加'?'

*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

.*?
. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?x

就是取前面任意长度的字符,直到一个x出现

常规练习:

1、 匹配一段文本中的每行的邮箱 查看详细说明
      http://blog.csdn.net/make164492212/article/details/51656638

2、 匹配一段文本中的每行的时间字符串,比如:‘1990-07-12’;

   分别取出1年的12个月(^(0?[1-9]|1[0-2])$)、
   一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$

3、 匹配qq号。(腾讯QQ号从10000开始)  [1,9][0,9]{4,}

4、 匹配一个浮点数。       ^(-?d+)(.d+)?$   或者  -?d+.?d*

5、 匹配汉字。 匹配全文是汉子的      ^[u4e00-u9fa5]{0,}$ 
分组命名--匹配标签
import re
# 分组命名(?P<组名>正则) (?P=组名)   一定不能忘记()分组

ret = re.search("<(?P<tag_name>w+)>w+</(?P=tag_name)>","<h1>hello</h1>")
#还可以在分组中利用?<name>的形式给分组起名字
#获取的匹配结果可以直接用group('名字')拿到对应的值
print(ret.group('tag_name'))  #结果 :h1
print(ret.group())  #结果 :<h1>hello</h1>

ret = re.search(r"<(w+)>w+</1>","<h1>hello</h1>")
#如果不给组起名字,也可以用序号来找到对应的组,表示要找的内容和前面的组内容一致
但是注意1等在python中有特殊的意义,要让其转义掉,所以前边要加一个r
#获取的匹配结果可以直接用group(序号)拿到对应的值
print(ret.group(1))
print(ret.group())  #结果 :<h1>hello</h1>

re模块

findall会匹配字符串中所有符合规则的项并返回一个列表,如果未匹配到返回空列表

import re
regex
ret = re.findall('d+','alex83')#效率低
print(ret)
# ret = re.findall('d(d)','aa1alex83')
# # findall遇到正则表达式中的分组,会优先显示分组中的内容
# print(ret)
#取消优先显示分组(?:正则)

练习 取整数
# 有的时候我们要匹配的内容是包含在不想要的内容之中的,
    # 只能先把不想要的内容匹配出来,然后再想办法从结果中去掉
  
ret = re.findall('d+','1-2*(60+(-40.35/5)-(-4*3))')
print(ret) #['1', '2', '60', '40', '35', '5', '4', '3']
#这一步只能把数字取出来,不能区分小数是什么,是不是正负数
ret = re.findall('-?d+.d*|(-?d+)','1-2*(60+(-40.35/5)-(-4*3))')
print(ret) # ['1', '-2', '60', '', '5', '-4', '3']
#这一步(-?d+)外边的括号很精髓,小数也取值,但是显示,只显示货号中的内容,如果没有这个()结果是:
#['1', '-2', '60', '-40.35', '5', '-4', '3']
ret.remove('')
print(ret)
#这一步,虽然显示括号里的内容,但是用‘’来占位,表示这有东西,只是没显示,所以需要remove掉  但是这里remove只能删除到从左到有的第一个目标,没法实现全部删除

#所以需要重新想办法,函数filter()筛选列表函数
ret = filter(lambda n:n, ['1', '-2', '60', '', '5', '-4', '3',''])
这样就能达到目的

search 如果能匹配上返回一个对象,如果不能匹配上返回None,通过group取值

# ret = re.search('d+','alex83')
# print(ret) 
# if ret:
#     print(ret.group()) # 如果是对象,那么这个对象内部实现了group,所以可以取值
#                        # 如果是None,那么这个对象不可能实现了group方法,所以报错
# 会从头到尾从带匹配匹配字符串中取出第一个符合条件的项
# 如果匹配到了,返回一个对象,用group取值
# 如果没匹配到,返回None,不能用group

match会从头匹配字符串中取出从第一个字符开始是否符合规则,如果符合,就返回对象,用group取值,如果不符合,就返回None

re.match
ret = re.match('d','alex83') == re.match('^d','alex83')
print(ret.group())
# match = search + ^正则

finditer生成迭代器占用计算机空间小(节省内存空间)

# ret = re.finditer('d','safhl02urhefy023908'*20000000)  # ret是迭代器
# for i in ret:    # 迭代出来的每一项都是一个对象
#     print(i.group())  # 通过group取值即可

compile预编译一个正则节省重复编写的时间

compile
s = '<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>'
ret = re.compile('d3')
print(ret)
r1 = ret.search('alex83')
print(r1)
ret.findall('wusir74')
ret = re.compile('d+')
r3 = ret.finditer('taibai40')
for i in r3:
    print(i.group())

先compile(如果没有重复使用同一个正则,也不能节省时间)
再finditer  从时间和空间上都节省了
ret= re.compile('d+')
res = ret.finditer('agks1ak018as093')
for r in res:
    print(r.group())

split通过正则表达式匹配的内容进行分割

# ret = re.split('d(d)','alex83wusir74taibai')  # 默认自动保留分组中的内容
# print(ret)
结果['alex', '3', 'wusir', '4', 'taibai']

sub替换,通过正则表达式匹配的内容进行替换

ret = re.sub('d','D','alex83wusir74taibai')
print(ret)
结果:alexDDwusirDDtaibai
ret = re.sub('d','D','alex83wusir74taibai',1)
print(ret)
结果:alexD3wusir74taibai

subn替换在sub的基础上,返回一个元组,第一个内容是替换结果,第二个是替换次数

ret = re.subn('d','D','alex83wusir74taibai')
print(ret)
结果:('alexDDwusirDDtaibai', 4)
  • 分组命名:(?P<名字>正则)
  • 引用分组:(?P=组命) 表示这个组中的内容必须和之前已经存在的一组匹配到的内容完全一致
  • 分组取值 search group('组名')
  • 1再正则中表示显示第一组的内容*************
#标签的匹配
exp = <h1>sdfssdsad</h1><h2>sfsdfsfs</h2>
import re
re.findall('<w+>(.*?)</w>',exp)

ret = re.search(r'<(w+)>(.*?)</1>',exp)
ret.group(2)

#用户输入身份证号匹配
inp = input('>')
ret = re.match('^[1-9]d{14}(d{2}[dx])?$',inp)
print(ret.group())

#匹配年月日日期,格式2018.12.9或2017-12-09
[1-9]d{3}(?P<sub>[^d])(1[0-2]|0?[1-9])(?P=sub)([12]d|3[01]|0?[1-9])

#匹配邮箱地址
#邮箱规则
# @之前必须有内容且只能是字母(大小写)、数字、下划线(_)、减号(-)、点(.)
# @和最后一个点(.)之间必须有内容且只能是字母(大小写)、数字、点(.)、减号(-),且两个点不能挨着
# 最后一个点(.)之后必须有内容且内容只能是字母(大小写)、数字且长度为大于等于2个字节,小于等于6个字节
如:taibai@oldboy.cn
[-w.]+@([-da-zA-Z]+.)+[a-zA-Zd]{2,6}

#ret = re.findall(par,content,flags=re.S)
flags=re.S#表示匹配换行
# 例题
    # 有的时候我们想匹配的内容包含在不相匹配的内容当中,这个时候只需要把不想匹配的先匹配出来,再通过手段去掉
import re
ret=re.findall(r"d+.d+|(d+)","1-2*(60+(-40.35/5)-(-4*3))")
print(ret)
ret.remove('')
print(ret)
  • re模块的爬虫例子
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)
    f = open("move_info7", "a", encoding="utf8")
    for obj in ret:
        print(obj)
        data = json.dumps(obj, ensure_ascii=False)
        f.write(data + "
")


if __name__ == '__main__':
    count = 0
    for i in range(10):
        main(count)
        count += 25
re模块中的flags参数
flags有很多可选值:

re.I(IGNORECASE)忽略大小写,括号内是完整的写法
re.M(MULTILINE)多行模式,改变^和$的行为
re.S(DOTALL)点可以匹配任意字符,包括换行符
re.L(LOCALE)做本地化识别的匹配,表示特殊字符集 w, W, , B, s, S 依赖于当前环境,不推荐使用
re.U(UNICODE) 使用w W s S d D使用取决于unicode定义的字符属性。在python3中默认使用该flag
re.X(VERBOSE)冗长模式,该模式下pattern字符串可以是多行的,忽略空白字符,并可以添加注释

原文地址:https://www.cnblogs.com/zheng0907/p/12488895.html