正则表达式与Python中re模块的使用
正则表达式与Python中re模块的使用
最近做了点爬虫,正则表达式
使用的非常多,用Python做的话会用到re模块
。
本文总结一下正则表达式
与re模块
的基础与使用。
另外,给大家介绍一个在线测试正则表达式的神器网站
:http://tool.chinaz.com/regex
大家可以去这里练习正则表达式
正则表达式
使用场景
关于正则表达式
的基本概念这里就不赘述了,大家可以去各种百科里查找它的定义。正则的使用场景
主要分为两个:
一是:检测某一段字符串是否符合规则,也就是我们常说的"校验"
二是:从一大段字符串中找到符合规则的字符串,可以理解为"检索"
对于第一种使用场景,我们在登陆或者注册时填写邮箱、手机号等内容的时候经常会见到——底层的实现思路就是利用正则“校验”我们输入的内容是否“规范”。
另外,正则的“检索”功能大量使用在爬虫
里,简单的说,爬虫
能从一个网站大量的数据中得到用户想要的内容等等......
需要特别注意,正则表达式只是用来处理字符串的
元字符
一:可以灵活使用的元字符:[] 与 [^]
(1)数字 [0-9]
(2)小写字母 [a-z]
(3)大写字母 [A-Z]
(4)大小写字母 [A-Za-z]
(5)大小写字母+数字 [0-9A-Za-z]
(6)注意一个字符组不限制个数:匹配三个的话:[0-9A-Za-z][0-9A-Za-z][0-9A-Za-z]
(7)大小写字母+ _与% [A-Za-z_%]
(8-1)匹配1-5 [1\-5] 用转义符,- 有特殊含义
(8-2)字符组中 - 是有特殊意义的,需要使用\作为转义符!
(9)[^123]——除了123都匹配,包括换行
二:匹配字符
(1)\d——所有数字
(2)\w——字母数字下划线
(3)\s——空白
(4)空格匹配空格
(5)\t——制表符
(6)\n——换行符
(7)\b——单词的边界 o\b——hello的'o'(o是单词的最后一个) \bo——ok的'o'(o是单词的第一个)
(8)\W——除了字母数字下划线
(9)\D——除了数字
(10)\S——除了空白符
(11).——匹配除了换行符所有的
(12)[\D\d]、[\W\w]、[\s\S]——匹配所有的
(13)[^123]——除了123都匹配,包括换行
(14)^——开始字符
(15)$——结束字符
(16)^.....$——开始+结尾,中间是5个除了换行的符号
(17)|——或 长的放在前面!
(18)()——分组 www\.(baidu|oldboy)\.com 匹配www.baidu.com或者www.oldboy.com
量词
量词用来限制匹配的次数
(1){n}——匹配n次
(2){n,}——匹配至少n次
(3){n,m}——匹配n到m次
(4)+——一次或者多次 匹配小数点的前后必须有数 \d+\.\d+
(5)*——0次或者多次 匹配整数:第一位是1-9不要是0: [1-9]\d*|0
(6)?——0次或者一次 匹配一个整数或者小数: \d+(\.\d+)?
贪婪匹配与惰性匹配
在正则中,默认的匹配模式是“贪婪匹配”,也就是说,在符合匹配规则的前提下尽可能多的去匹配字符
,但是有些时候,我们不需要匹配太多的内容,只要得到需要的“片段”内容就好了。
而量词
是匹配多次的,这时我们可以在量词的后面加上?
就可以让匹配“适可而止”了,这样的匹配规则就是惰性匹配
这里举一个贪婪匹配
与惰性匹配
对比的例子:
有字符串"aasdasdasdxxxxxxasdasd",现在想匹配到x结束
(1)用"惰性匹配"的话:
语法:a.*?x
说明:遇见一个先检测是不是x,如果是的话就停止,不是的话就匹配
结果:aasdasdasdx
(2)默认的"贪婪匹配":
语法:a.*x
说明:这里会匹配后面所有的x
结果:aasdasdasdxxxxxx
当然,什么时候用贪婪匹配
什么时候用惰性匹配
需要我们具体情况具体分析。
re模块
re模块
是Python的内置模块,是专门用来处理与正则表达式相关需求的模块。
使用方法:直接在Python程序中import即可:
import re
根据正则的规则从一段内容中查找结果
findall
找到所有符合正则规则的字符串,并以列表的形式返回结果:
ret = re.findall('\d+','whw123w1233')
print(ret)
#结果:
['123', '1233']
search
找到第一个,返回结果集
,需要通过group方法取值。没取到的话返回None,此时用group方法会报错!因此需要提前判断下:
ret = re.search('\d+','wanghw123ww')
print(ret)
if ret:
print(ret.group())
#结果:
<_sre.SRE_Match object; span=(6, 9), match='123'>
123
match
从头开始找第一个,返回一个结果集,需要通过group取值。没取到的话返回None,此时用group方法会报错!因此需要提前判断下:
下面是匹配到结果的:
ret = re.match('\d+','23whw22a')
print(ret)
if ret:
print(ret.group())
#结果:
<_sre.SRE_Match object; span=(0, 2), match='23'>
23
没有匹配到的话可以利用异常处理或者像上面一样的条件判断:
ret = re.match('\d+','whw22a')
print(ret)
try:
print(ret.group())
except AttributeError as e:
print(e,':','没有匹配到!')
#结果:
None
'NoneType' object has no attribute 'group' : 没有匹配到!
我们可以看到,match的返回结果与search是一样的;而且,match的匹配语法其实就是相当于search在其匹配规则上加上^
,因此:match可以被search代替
,实际中match用的比较少。
re.search('^\d+','123asd')
等同于:
re.match('\d+','123asd')
替换与切割
sub
我们可以利用re模块与正则表达式结合进行字符串的批量替换:
有如下需求:
字符串:s1 = 'wanghw123whw456',将s1中所有的数字替换成字符串"HERO"。
我们可以利用re模块的sub方法来做:
s1 = 'wanghw123whw456'
ss = re.sub('\d+','HERO',s1)
print(ss)
#结果:
wanghwHEROwhwHERO
sub方法的第一个参数是正则的规则,第二个参数是要被替换成的字符串,第三个参数是需要被操作的字符串。当然,聪明的你肯定想到一个问题:这默认替换的是所有符合规则的字符串呀!我如果想限定替换的次数
怎么做呢?我们可以指定第四个参数,来达到限定替换次数的作用:
s1 = 'wanghw123whw456'
ss = re.sub('\d+','HERO',s1,1)
print(ss)
#结果:
wanghwHEROwhw456
subn
subn的用法跟sub一模一样,只不过返回值
是有区别的,我们可以执行一下看看:
s1 = 'wanghw123whw456'
ss = re.subn('\d+','HERO',s1)
print(ss)
#结果:
('wanghwHEROwhwHERO', 2)
当然也可以指定替换的次数:
s1 = 'wanghw123whw456'
ss = re.subn('\d+','HERO',s1,1)
print(ss)
#结果:
('wanghwHEROwhw456', 1)
大家可以看到,subn方法返回一个元组:第一个元素是得到的结果,第二个元素是替换的次数。
split
split方法与字符串的切割方法一样,返回的也是一个列表,只不过,这次我们用的是正则匹配的结果
进行切割的!
s1 = 'wanghw123whw456qwe'
sq = re.split('\d+',s1)
print(sq)
#结果:
['wanghw', 'whw', 'qwe']
split的第一个参数放正则规则,第二个参数放被操作的字符串。
预编译
我们都知道,Python是一种解释型的编程语言,Python的代码需要先在解释器中转换为机器码,最终转换为计算机能识别的编码才能运行。
对于正则匹配来说,如果一个同样的正则表达式需要用到几十次甚至上百次去匹配一段长文字的话,用普通的方法,我们需要将这个正则表达式运行几十次甚至上百次来进行每一次的匹配。这样大大的降低了我们程序的效率,是我们最不愿意看到的!
而在Python中为我们提供了预编译
的方法去解决这个难题。
所谓的预编译
,在正则中我们实际用在下面这样的场景中:对于一个经常被重复使用的正则表达式,我们可以先进行一次编译,将这个正则表达式需要做的事预先编译
,这样,之后只要用到了这个表达式我们可以将编译好的结果
直接拿来用就行了,酱紫大大的省了代码的执行时间!
预编译的使用举例:
比如说,我们想匹配三段(实际中可能是上百个)用到相同规则匹配的字符串中的内容。首先,我们可以将这个规则进行预编译,然后利用这个预编译的结果去匹配每一段需要匹配的字符串:
ss = 'wangh123qwe3123'
ss1 = 'wangh123qwe3123312'
ss2 = 'wangh123qwe3123qwqasd5412'
par = re.compile('\d+')
print(par)
rets = par.findall(ss)
rets1 = par.findall(ss1)
rets2 = par.findall(ss2)
print(rets)
print(rets1)
print(rets2)
看到这里,相信聪明的你又发现问题了!这样固然降低了时间复杂度
,但是,如果实际中有几万个大字符串,你这样一下子读取出来,内存不是直接爆炸了么!
对!没错,针对降低空间复杂度
的问题,Python为我们提供了另外一个方法————finditer
finditer
需要注意的是,finditer方法得到的是一个迭代器
。下面我们模拟匹配10个相同字符串的例子:
re= re.finditer('\d+','23adq9009qwe'*10)
print(re)
for i in re:
print(i)
print(i.group())
#结果(只拿一个结果举例):
#第一个拿到的是re——迭代器对象
<callable_iterator object at 0x000001157E942940>
#遍历re中拿到的是"结果集"
<_sre.SRE_Match object; span=(0, 2), match='23'>
#最终的结果需要用group()方法取到:
23
下面就来一发compile与finditer结合同时降低时间复杂度与空间复杂度的方法
:
par = re.compile('\d+')
ret = par.finditer('wanghe123asdh2312asd33'*10)
print(ret)
for i in ret:
print(i.group())
#结果(只拿前1组):
<callable_iterator object at 0x000001DA20362940>
123
2312
33
“分组”与re模块的结合使用
在实际中,正则表达式的“分组”思想与re模块的方法结合使用的情况非常多
findall与分组
有如下需求:在标签字符串中提取出标签中的内容:
一般的方法是这样的:
s = '<title>whwHERO</title>'
print(ret)
print(ret[0].split('>'))
print(ret[0].split('>')[1].split('<')[0])
#结果:
['>whwHERO<']
['', 'whwHERO<']
whwHERO
这样的方法看起来比较麻烦,正则匹配到以后我们还得用Python的方法去处理。
我们可以利用正则的分组
与re模块的方法结合区实现:
ret = re.findall('>(\w+)<',r'<title>whwHERO<\\title>')
print(ret)
#结果:
['whwHERO']
我们可以看到:findall优先显示分组中的内容
!简直不要太棒!
这样的效果在本例中效果很棒,但是,在一下情况下我们是不想酱紫的:
如果我们想匹配www.baidu.com或者www.taobao.com。正则表达式利用分组我们可以酱紫写:www\.(baidu|taobao)\.com
但是,与findall方法结合会出现问题:
ret = re.findall('www\.(baidu|taobao)\.com','www.baidu.com')
print(ret)
#结果:
['baidu']
再比如,我们想获取一串字符串中的数字(包括小数):
digit = re.findall('\d+(\.\d+)?',r'1.23+2.33')
print(digit)
#结果:['.23', '.33']
很显然,酱紫的分组优先
是我们不想要的。那么如何取消findall下的分组优先呢?
在分组里的最前面加上?:
就可以了,上面两个例子我们可以酱紫修改:
ret = re.findall('www\.(?:baidu|taobao)\.com','www.baidu.com')
print(ret)
#结果:['www.baidu.com']
digit = re.findall('\d+(?:\.\d+)?','1.23+2.33')
print(digit)
#结果:
['1.23', '2.33']
split与分组
split与分组结合跟普通的split方法的区别是:与分组结合的话会保留在分组中“切掉”的内容,用一个例子来讲就很直观了:
ret = re.split('\d(\d)','wanghw211whw222www233ee')
print(ret)
#结果:
['wanghw', '1', '1whw', '2', '2www', '3', '3ee']
我们可以看到:匹配的第二个数字在分组中,我们利用匹配到的两个数字进行切割,但是由于只有第二个数字在分组中,因此第一个数字被“干掉”了,切割后的列表只保留了第二个数字!
search与分组
在讲search与分组的关系时,请大家记住下面做数据处理的思想:
在爬虫\数据清洗的过程中最常用的正则表达式的操作
而大多数时候我们并不是把我们要的内容都用正则写出来
而是把整个页面都用正则描述下来,然后把我需要的内容放在分组里
这样就能够通过分组取到我想要的内容了
(心中默念一遍后)看下面这个例子:
比如说我们从网页中爬取了下面字符串(当然实际中要大得多,这里只做模拟)
s="<title>whwisahero<\title>"
我们想用search方法获取里面的内容,根据上面介绍的处理数据的思路
,我们先利用search拿到结果集
,然后在这个结果集中取数据:
ret = re.search(r'<(\w+)>(\w+)<\\(\w+)>',r'<title>whwisahero<\title>')
print(ret.group())
print(ret.group(0))
print(ret.group(1))
print(ret.group(2))
print(ret.group(3))
#结果:
<title>whwhwhw<\title>
<title>whwhwhw<\title>
title
whwisahero (这是我们想要的内容)
title
酱紫我们就拿到了结果。
但是,相信聪明的你叕叕叕叕叕发现问题了!我怎么知道我想要的内容在哪个索引里!
没错!这里就需要我们利用分组命名
了!
ret = re.search('<(?P<title>\w+)>(?P<content>\w+)</(?P<title2>\w+)>',r'<title>whwisahero</title>')
print(ret.group('title'))
print(ret.group('title2'))
print(ret.group('content'))
#结果:
title
title
whwisahero
可以看到,我们在分组内部的前面利用?P<分组名>
的语法为分组起了个名字,在最后取值的时候将分组名加在group方法的参数里就OK了!
另外,分组名还可以这么玩:
如果两个分组名一样可以酱紫写:
ret1 = re.search('<(?P<title>\w+)>(?P<content>\w+)</(?P=title)>',r'<title>whwisahero</title>')
print(ret1.group('title'))
print(ret1.group('content'))
#结果:
title
whwisahero
也可以酱紫写:
ret2 = re.search(r'<(?P<title>\w+)>(?P<content>\w+)</\1>',r'<title>whwisahero</title>')
print(ret2.group('title'))
print(ret2.group('content'))
#结果:
title
whwisahero
筛选数据的思想
接着跟大家下筛选数据的思路:
当我们要匹配的内容混在不想匹配的内容中
只能把不想要的也匹配出来,然后去掉不想要的就是想要的
看下面的例子:
我想拿到一串字符串中的正整数,利用上面的思路可以酱紫做:先将不需要的数据也匹配出来,然后进行进一步的加工。因为实际中正则表达式并不是万能或者一劳永逸的,得到的数据还需要我们进行进一步的处理:
ret = re.findall('-*\d+\.\d+|(-*\d+)','2*(60+(-40.35/5)-(-4*3))')
print(ret)#['2', '60', '', '5', '-4', '3']
for i in ret:
if not i or i.startswith('-'):
ret.remove(i)
print(ret)#['2', '60', '5', '3']
常见的几个正则表达式例子:
最后给大家分享常见的几个正则表达式:
#邮箱的规则:
(1)@之前必须有内容且只能是字母(大小写)、数字、下划线(_)、减号(-)、点(.)
(2)@和最后一个点(.)之间必须有内容且只能是字母(大小写)、数字、点(.)、减号(-),且两个点不能挨着
(3)最后一个点(.)之后必须有内容且内容只能是字母(大小写)、数字且长度为大于等于2个字节,小于等于6个字节
正则表达式:[\w\-\.]+@([a-zA-Z\d\-]+\.)+[a-zA-Z\d]{2,6}
1.整数或者小数 包括正数和负数
-?\d+(\.\d+)?
2.年月日
2018-9-20
\d{1,4}-\d{1,2}-\d{1,2}
\d{1,4}-(1[0-2]|0?[1-9])-(3[01]|[12]\d|0?[1-9])
3.匹配qq号
4位 11位
[1-9]\d{4,11}
4.8-10位的密码 数字字母下划线
\w{8,10}
5.验证码
[\da-zA-Z]{4}
当然,这些写法并不是固定的,大家多去上面介绍的网站上多练习,毕竟勤能补拙是良训,一分辛苦一分才
正则表达式与Python中re模块的使用的更多相关文章
- 正则表达式与python中re模块
一个网站,正则表达式入门的,很好 http://www.jb51.net/tools/zhengze.html 下面这个包含对python中re的介绍,也是很不错的http://www.w3cscho ...
- 常用正则表达式与python中的re模块
正则表达式是一种通用的字符串匹配技术,不会因为编程语言不一样而发生变化. 部分常用正则表达式规则介绍: . 匹配任意的一个字符串,除了\n * 匹配任意字符串0次或者任意次 \w 匹配字母.数字.下划 ...
- python全栈开发之正则表达式和python的re模块
正则表达式和python的re模块 python全栈开发,正则表达式,re模块 一 正则表达式 正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的 ...
- python 历险记(五)— python 中的模块
目录 前言 基础 模块化程序设计 模块化有哪些好处? 什么是 python 中的模块? 引入模块有几种方式? 模块的查找顺序 模块中包含执行语句的情况 用 dir() 函数来窥探模块 python 的 ...
- 正则表达式和python的re模块
0 正则表达式 0.1 常见的元字符 .: 匹配除\r\n之外的任何单个字符 *: 匹配前面的子表达式任意次,例如Zz*可以匹配Z,可以匹配Zz,也可以匹配Zzzzzzzzzz +: ...
- 第11.18节 Python 中re模块的匹配对象
匹配对象是Python中re模块正则表达式匹配处理的返回结果,用于存放匹配的情况.老猿认为匹配对象更多的应该是与组匹配模式的功能对应的,只是没有使用组匹配模式的正则表达式整体作为组0. 为了说明下面的 ...
- Python中optionParser模块的使用方法[转]
本文以实例形式较为详尽的讲述了Python中optionParser模块的使用方法,对于深入学习Python有很好的借鉴价值.分享给大家供大家参考之用.具体分析如下: 一般来说,Python中有两个内 ...
- python中threading模块详解(一)
python中threading模块详解(一) 来源 http://blog.chinaunix.net/uid-27571599-id-3484048.html threading提供了一个比thr ...
- 【转】关于python中re模块split方法的使用
注:最近在研究文本处理,需要用到正则切割文本,所以收索到了这篇文章,很有用,谢谢原作者. 原址:http://blog.sciencenet.cn/blog-314114-775285.html 关于 ...
随机推荐
- Request.ServerVariables参数说明
Request.ServerVariables["SERVER_NAME"] '获取服务器IP Request.ServerVariables["HTTP_REFERER ...
- 剑指Offer 65. 矩阵中的路径 (回溯)
题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路径经过了矩阵中 ...
- Spring XML文件配置
singleton不能创建多对象(默认) <?xml version="1.0" encoding="UTF-8"?> <beans xmln ...
- Java 8 Lambda 表达式(二)
lambdas 实现 Runnable 接口 下面是使用 lambdas 来实现 Runnable 接口的示例: // 1.1使用匿名内部类 new Thread(new Runnable() { @ ...
- @RequestParam 和 @ PathVariable 的区别
@RequestParam 和 @ PathVariable 的区别http://localhost:8080/Springmvc/user/page.do?pageSize=3&pageNo ...
- React-redux深入理解
首先,一张 Redux 解释图镇楼: [回顾]Redux 的核心: store 是什么?(createStore 函数的实现) const store = createStore(reducer); ...
- Apache HTTP 服务器 2.4(又名httpd)安装\配置 \启动
Apache HTTP 服务器 2.4 源文档
- 博客作业06--结构体&指针
1.本章学习总结 1.1思维导图 1.2.本章学习体会 结构体突破了数组的局限,把不同类型有内在联系的数据汇聚成一个整体,这种新的构造数据类型,提供了更便利的手段,更好的实现代码功能.通过代码建立文件 ...
- git push 不想把本地某个目录下文件上传的办法
- ROS * 了解学习urdf的内容格式及编写
<?xml version="1.0" ?> 声明文件使用xml描述 <robot name="robot_name">定义这是一个机器 ...