python的re模块(正则)

re:一些带有特殊含义的符号或者符号的组合
为什么要用re:一堆字符串中找到你所需要的内容,过滤规则是什么样,通过re模块功能来告诉计算机你的过滤规则
应用:在爬虫中最为常用;使用爬虫时有其他模块可以导入帮助clear数据,正则也可用于其他方面
原理:re模块的内部实现 不是python 而是调用了c库

单字符匹配:

# 匹配某个字符串:
# text = "abc"
# ret = re.match('b',text)
# print(ret.group())


# 点(.):匹配任意的字符(除了'\n'):
# text = "\nabc"
# ret = re.match('.',text)
# print(ret.group())


# \d:匹配任意的数字:
# text = "aab"
# ret = re.match('\d',text)
# print(ret.group())


# \D:匹配任意的非数字:
# text = "cab"
# ret = re.match('\D',text)
# print(ret.group())


# \s:匹配的是空白字符(包括:\n,\t,\r和空格):
# text = " ab"
# ret = re.match('\s',text)
# print("="*30)
# print(ret.group())
# print("="*30)


# \S:非空白字符:
# text = "\nab"
# ret = re.match('\S',text)
# print("="*30)
# print(ret.group())
# print("="*30)


# \w:匹配的是a-z和A-Z以及数字和下划线:
# text = "+bc"
# ret = re.match('\w',text)
# print("="*30)
# print(ret.group())
# print("="*30)


# \W:匹配的是和\w相反的:
# text = "1bc"
# ret = re.match('\W',text)
# print("="*30)
# print(ret.group())
# print("="*30)


# []组合的方式,只要满足中括号中的某一项都算匹配成功:
# text = "bc"
# ret = re.match('[1b]',text)
# print("="*30)
# print(ret.group())
# print("="*30)

# 使用组合的方式[0-9]\d:
# text = "abc"
# ret = re.match('[^0-9]',text)
# print("="*30)
# print(ret.group())
# print("="*30)

# 使用组合的方式实现\w:
text = "+bc"
ret = re.match('[^a-zA-Z0-9_]',text)
print("="*30)
print(ret.group())
print("="*30)

多字符匹配:

# *:匹配0个或者多个字符:
# text = "+abc"
# result = re.match('\D*',text)
# print(result.group())


# +:匹配1个或者多个字符:
# text = "1abc"
# result = re.match('\w+',text)
# print(result.group())


# ?:匹配前一个字符0个或者1个:
# text = "+abc"
# result = re.match('\w?',text)
# print(result.group())


# {m}:匹配m个字符:
# text = "+1abc"
# result = re.match('\w{2}',text)
# print(result.group())


# {m,n}:匹配m-n之间的个数的字符:
text = "1abc+"
result = re.match('\w{1,3}',text)
print(result.group())

正则表达式案例:

# 1. 验证手机号码:手机号码的规则是以1开头,第二位可以是34587,后面那9位就可以随意了。
# text = "18677889900"
# result = re.match("1[34587]\d{9}",text)
# print(result.group())


# 2. 验证邮箱:邮箱的规则是邮箱名称是用数字、英文字符、下划线组成的,然后是@符号,后面就是域名了。
# text = "[email protected]"
# result = re.match("\w+@[a-z0-9]+\.[a-z]+",text)
# print(result.group())


# 3. 验证URL:URL的规则是前面是http或者https或者是ftp然后再加上一个冒号,再加上一个斜杠,再后面就是可以出现任意非空白字符了。
# text = "https://baike.baidu.com/item/Python/407313?fr=aladdin"
# result = re.match("(http|https|ftp)://\S+",text)
# print(result.group())


# 4. 验证身份证:身份证的规则是,总共有18位,前面17位都是数字,后面一位可以是数字,也可以是小写的x,也可以是大写的X。
text = "36530019870716234x"
result = re.match("\d{17}[\dxX]",text)
print(result.group())

开始/结束/贪婪和非贪婪:

# ^:以...开头:
# text = "hello world"
# result = re.search("world",text)
# print(result.group())


# $:以...结尾:
# text = "hello world"
# result = re.search("hello$",text)
# print(result.group())
# text = ""
# result = re.search("^$",text)
# print(result.group())


# |:匹配多个字符串或者表达式:



# 贪婪和非贪婪:
# text = "12345"
# result = re.search("\d+?",text)
# print(result.group())


# 案例1:提取html标签名称:
# text = "<h1>这是标题</h1>"
# result = re.search("<.+?>",text)
# print(result.group())


# 案例2:验证一个字符是不是0-100之间的数字:
# 0,1,99,100
# 01
text = "101"
result = re.match("0$|[1-9]\d?$|100$",text)
print(result.group())

转义字符和原生字符串:

# Python中的转义字符:
# raw
# text = r"hello\nworld"
# print(text)


# 正则表达式中的转义字符:
# text = "apple price is $99,range price is $88"
# result = re.findall("\$\d+",text)
# print(result)


# 原生字符串和正则表达式:
# 正则表达式的字符串解析规则:
# 1. 先把这个字符串放在Python语言层面进行解析。
# 2. 把Python语言层面解析的结果再放到正则表达式层间进行解析。
text = "\cba c"
# result = re.match("\\\\c",text) # \\\\c =(Python语言层面)> \\c =(正则表达式层面)> \c
result = re.match(r"\\c",text) # \\c =(正则表达式层面)> \c
print(result.group())

分组:

text = "apple price is $99,orange price is $88"
result = re.search('.+(\$\d+).+(\$\d+)',text)
print(result.groups())

# group()/group(0):匹配整个分组
# group(1):匹配第一个分组
# group(2):匹配第二个分组
# groups():获取所有的分组

re中常用的函数:

# findall:查找所有满足条件的
# text = "apple price is $99,orange price is $88"
# result = re.findall(r'\$\d+',text)
# print(result)

# sub:根据规则替换其他字符串
# text = "nihao zhongguo,hello world"
# new_text = text.replace(" ","\n")
# new_text = re.sub(r' |,','\n',text)
# print(new_text)
# html = """
# <div class="job-detail">
#     <p>1. 3年以上相关开发经验 ,全日制统招本科以上学历</p>
#     <p>2. 精通一门或多门开发语言(Python,C,Java等),其中至少有一门有3年以上使用经验</p>
#     <p>3. 熟练使用ES/mysql/mongodb/redis等数据库;</p>
#     <p>4. 熟练使用django、tornado等web框架,具备独立开发 Python/Java 后端开发经验;</p>
#     <p>5. 熟悉 Linux / Unix 操作系统&nbsp;</p>
#     <p>6. 熟悉 TCP/IP,http等网络协议</p>
#     <p>福利:</p>
#     <p>1、入职购买六险一金(一档医疗+公司全额购买商业险)+开门红+全额年终奖(1年13薪,一般会比一个月高)</p>
#     <p>2、入职满一年有2次调薪调级机会</p>
#     <p>3、项目稳定、团队稳定性高,团队氛围非常好(汇合员工占招行总员工比例接近50%);</p>
#     <p>4、有机会转为招商银行内部员工;</p>
#     <p>5、团队每月有自己的活动经费,法定节假日放假安排;</p>
#     <p>6、办公环境优良,加班有加班费(全额工资为计算基数,加班不超过晚上10点,平日加班为时薪1.5倍,周末加班为日薪2倍,周末加班也可优先选择调休,管理人性化)。</p>
# </div>
# """
# new_html = re.sub(r'<.+?>',"",html)
# print(new_html)


# split:根据规则分割字符串
# text = "nihao zhongguo,hello world"
# result = re.split(r' |,',text)
# print(result)


# compile:编译正则表达式
text = "apple price is 34.56"
# r = re.compile(r"""
# \d+ # 整数部分
# \.? # 小数点
# \d* # 小数部分
# """,re.VERBOSE)
# result = re.search(r,text)
result = re.search(r"""
\d+ # 整数部分
\.? # 小数点
\d* # 小数部分
""",text,re.VERBOSE)
print(result.group())

如果想要在正则表达式中加注释,那么需要在正则表达式的函数最后加一个re.VERBOSE

import re
print(re.findall('\w','ab 12\+- *&_'))
print(re.findall('\W','ab 12\+- *&_'))
print(re.findall('\s','ab \r1\n2\t\+- *&_'))
print(re.findall('\S','ab \r1\n2\t\+- *&_'))
print(re.findall('\d','ab \r1\n2\t\+- *&_'))
print(re.findall('\D','ab \r1\n2\t\+- *&_'))
print(re.findall('\w_sb','egon alex_sb123123wxx_sb,lxx_sb'))
print(re.findall('\Aalex','abcalex is salexb'))
print(re.findall('\Aalex','alex is salexb'))
print(re.findall('^alex','alex is salexb'))
print(re.findall('sb\Z','alexsb is sbalexbsb'))
print(re.findall('sb$','alexsb is sbalexbsb'))
print(re.findall('^ebn$','ebn1')) #^ebn$ 筛出的就是ebn(以ebn开头,以ebn结尾)
print(re.findall('a\nc','a\nc a\tc a1c'))
#	\t为制表符,在不同平台表示不同的空个数
#	\A  ^     #使用^
#	\Z  $     #使用$

重复匹配:

#. ? * + {m,n} .* .*? {m}
1、.:代表除了换行符外的任意一个字符
. 除了换行符之外的任意一个字符, 如果想不除换行符,后加re.DOTALL

print(re.findall('a.c','abc a1c aAc aaaaaca\nc'))
print(re.findall('a.c','abc a1c aAc aaaaaca\nc',re.DOTALL))

2、?:代表左边那一个字符重复0次或1次 默认贪婪(能匹配1次绝不匹配0次)
?不能单独使用

print(re.findall('ab?','a ab abb abbb abbbb abbbb'))

3、*:代表左边那一个字符出现0次或无穷次 默认贪婪(能匹配无穷次绝不匹0次)

print(re.findall('ab*','a ab abb abbb abbbb abbbb a1bbbbbbb'))

4、+ :代表左边那一个字符出现1次或无穷次 默认贪婪(能匹配无穷次绝不匹配1次)

print(re.findall('ab+','a ab abb abbb abbbb abbbb a1bbbbbbb'))

5、{m,n}:代表左边那一个字符出现m次到n次 默认贪婪(能匹配n次绝不匹配m次)

print(re.findall('ab?','a ab abb abbb abbbb abbbb'))
print(re.findall('ab{0,1}','a ab abb abbb abbbb abbbb'))
print(re.findall('ab*','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab{0,}','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab+','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab{1,}','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab{1,3}','a ab abb abbb abbbb abbbb a1bbbbbbb'))

6、.*:匹配任意长度,任意的字符=====》贪婪

print(re.findall('a.*c','ac a123c aaaac a *123)()c asdfasfdsadf'))

7、.*?:多少次到多少次(贪婪)+ ?=非贪婪

print(re.findall('a.*?c','a123c456c'))

():分组

print(re.findall('(alex)_sb','alex_sb asdfsafdafdaalex_sb'))
print(re.findall(
    'href="(.*?)"',
    '<li><a id="blog_nav_sitehome" class="menu" href="http://www.cnblogs.com/">博客园</a></li>')
)

[]:匹配一个指定范围内的字符(这一个字符来自于括号内定义的)
[] 内写什么就是其单独的意义, 可写0-9 a-zA-Z

print(re.findall('a[0-9][0-9]c','a1c a+c a2c a9c a11c a-c acc aAc'))

当-需要被当中普通符号匹配时,只能放到[]的最左边或最 右边
a-b有特别的意思,所以如果想让-表示它本身,要将其放在最左或最右

print(re.findall('a[-+*]c','a1c a+c a2c a9c a*c a11c a-c acc aAc'))
print(re.findall('a[a-zA-Z]c','a1c a+c a2c a9c a*c a11c a-c acc aAc'))
# 	[]内的^代表取反的意思 (^在[]中表示取反)
print(re.findall('a[^a-zA-Z]c','a c a1c a+c a2c a9c a*c a11c a-c acc aAc'))
print(re.findall('a[^0-9]c','a c a1c a+c a2c a9c a*c a11c a-c acc aAc'))
print(re.findall('([a-z]+)_sb','egon alex_sb123123wxxxxxxxxxxxxx_sb,lxx_sb'))
# 	| :或者
print(re.findall('compan(ies|y)','Too many companies have gone bankrupt, and the next one is my company'))

(?: ):代表取匹配成功的所有内容,而不仅仅只是括号内的内容 ((?: )表示匹配的结果都要,不单单要()内的)

print(re.findall('compan(?:ies|y)','Too many companies have gone bankrupt, and the next one is my company'))
print(re.findall('alex|sb','alex sb sadfsadfasdfegon alex sb egon'))

re模块的其他方法:

print(re.findall('alex|sb','123123 alex sb sadfsadfasdfegon alex sb egon'))
print(re.search('alex|sb','123213 alex sb sadfsadfasdfegon alex sb egon').group())
print(re.search('^alex','123213 alex sb sadfsadfasdfegon alex sb egon'))
print(re.search('^alex','alex sb sadfsadfasdfegon alex sb egon').group())
#	re.search, 取第一个结果,若没有返回None;若想让结果直接显示后加group();返回None时用group()会报错
#	re.match 相当于^版本的search
print(re.match('alex','alex sb sadfsadfasdfegon alex sb egon').group())
print(re.match('alex','123213 alex sb sadfsadfasdfegon alex sb egon'))
#	re.match 相当于^版本的search
#	re.split与split相比,内部可以使用正则表达式
info='a:b:c:d'
print(info.split(':'))
print(re.split(':',info))
info=r'get :a.txt\3333/rwx'
print(re.split('[ :\\\/]',info))
#	re.sub 与replace相比,内部可以使用正则表达式
print('egon is beutifull egon'.replace('egon','EGON',1))
print(re.sub('(.*?)(egon)(.*?)(egon)(.*?)',r'\1\2\3EGON\5','123 egon is beutifull egon 123'))
print(re.sub('(lqz)(.*?)(SB)',r'\3\2\1',r'lqz is SB'))
print(re.sub('([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)',r'\5\2\3\4\1',r'lqzzzz123+ is SB'))

太常用的表达式可以做成

pattern=re.compile('alex')
print(pattern.findall('alex is alex alex'))
print(pattern.findall('alexasdfsadfsadfasdfasdfasfd is alex alex'))

断言
正则表达式中的断言,作为高级应用出现,倒不是因为它有多难,而是概念比较抽象,不容易理解而已。
如果不用断言,以往用过的那些表达式,仅仅能获取到有规律的字符串,而不能获取无规律的字符串。
举个例子,比如html源码中有xxx标签,用以前的知识,我们只能确定源码中的是固定不变的。因此,如果想获取页面标题(xxx),充其量只能写一个类似于这样的表达式:.<em></em>,而这样写匹配出来的是完整的xxx标签,并不是单纯的页面标题xxx。
想解决以上问题,就要用到断言知识。
在讲断言之前,读者应该先了解分组,这有助于理解断言。
分组在正则中用()表示,根据小菜理解,分组的作用有两个:
n 将某些规律看成是一组,然后进行组级别的重复,可以得到意想不到的效果。
n 分组之后,可以通过后向引用简化表达式。
先来看第一个作用,对于IP地址的匹配,简单的可以写为如下形式:
\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}
但仔细观察,我们可以发现一定的规律,可以把.\d{1,3}看成一个整体,也就是把他们看成一组,再把这个组重复3次即可。表达式如下:
\d{1,3}(.\d{1,3}){3}
这样一看,就比较简洁了。
再来看第二个作用,就拿匹配xxx标签来说,简单的正则可以这样写:
.
可以看出,上边表达式中有两个title,完全一样,其实可以通过分组简写。表达式如下:
<(title)>.</\1>
这个例子实际上就是反向引用的实际应用。对于分组而言,整个表达式永远算作第0组,在本例中,第0组是<(title)>.
</\1>,然后从左到右,依次为分组编号,因此,(title)是第1组。
用\1这种语法,可以引用某组的文本内容,\1当然就是引用第1组的文本内容了,这样一来,就可以简化正则表达式,只写一次title,把它放在组里,然后在后边引用即可。
以此为启发,我们可不可以简化刚刚的IP地址正则表达式呢?原来的表达式为\d{1,3}(.\d{1,3}){3},里边的\d{1,3}重复了两次,如果利用后向引用简化,表达式如下:
(\d{1,3})(.\1){3}
简单的解释下,把\d{1,3}放在一组里,表示为(\d{1,3}),它是第1组,(.\1)是第2组,在第2组里通过\1语法,后向引用了第1组的文本内容。
经过实际测试,会发现这样写是错误的,为什么呢?
小菜一直在强调,后向引用,引用的仅仅是文本内容,而不是正则表达式!
也就是说,组中的内容一旦匹配成功,后向引用,引用的就是匹配成功后的内容,引用的是结果,而不是表达式。
因此,(\d{1,3})(.\1){3}这个表达式实际上匹配的是四个数都相同的IP地址,比如:123.123.123.123。
至此,读者已经掌握了传说中的后向引用,就这么简单。
接下来说说什么是断言。
所谓断言,就是指明某个字符串前边或者后边,将会出现满足某种规律的字符串。
就拿文章开篇的例子来说,我们想要的是xxx,它没有规律,但是它前边肯定会有,后边肯定会有,这就足够了。
想指定xxx前肯定会出现,就用正后发断言,表达式:(?<=<title>).*<br/> 向指定xxx后边肯定会出现,就用正先行断言,表达式:.(?=)
两个加在一起,就是(?<=).(?=)
这样就能匹配到xxx。

   其实掌握了规律,就很简单了,无论是先行还是后发,都是相对于xxx而言的,也就是相对于目标字符串而言。
   假如目标字符串后边有条件,可以理解为目标字符串在前,就用先行断言,放在目标字符串之后。
   假如目标字符串前边有条件,可以理解为目标字符串在后,就用后发断言,放在目标字符串之前。
   假如指定满足某个条件,就是正。
   假如指定不满足某个条件,就是负。
   断言只是条件,帮你找到真正需要的字符串,本身并不会匹配!

(?=X )
零宽度正先行断言。仅当子表达式 X 在 此位置的右侧匹配时才继续匹配。例如,/w+(?=/d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。
(?!X)
零宽度负先行断言。仅当子表达式 X 不在 此位置的右侧匹配时才继续匹配。例如,例如,/w+(?!/d) 与后不跟数字的单词匹配,而不与该数字匹配 。
(?<=X)
零宽度正后发断言。仅当子表达式 X 在 此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后面的 99 的实例匹配。此构造不会回溯。
(?<!X)
零宽度负后发断言。仅当子表达式 X 不在此位置的左侧匹配时才继续匹配。例如,(?<!19)99 与不跟在 19 后面的 99 的实例匹配

猜你喜欢

转载自blog.csdn.net/weixin_41130251/article/details/103560686
今日推荐