Python 正则表达式(匹配分组)

仅供学习,转载请注明出处

匹配分组

字符

功能

|

匹配左右任意一个表达式

(ab)

将括号中字符作为一个分组

\num

引用分组num匹配到的字符串

(?P<name>)

分组起别名

(?P=name)

引用别名为name分组匹配到的字符串

匹配左右任意一个表达式,类似或条件: |

我们在查询东西的时候不一定就是查一样,可能还会想要同时查询另一样东西。那么前面的只是讲述了匹配查询一样的情况。

需求:匹配出0-100之间的数字

#coding=utf-8

import re

In [3]: re.match('[1-9]?\d','8').group()                                       
Out[3]: '8'

In [5]: re.match('[1-9]?\d','78').group()                                      
Out[5]: '78'

# 不正确的情况,因为[1-9]无法匹配0,那么直接就是使用\d匹配到0就结束了,所以只会打印一个0,不会打印8出来。
In [6]: re.match('[1-9]?\d','08').group()                                      
Out[6]: '0'

# 修正之后的,由于[1-9]无法匹配0,那么报错的时候ret为空,直接打印不在0-100之间
In [14]: ret = re.match('[1-9]?\d$|100','08')                                  

In [15]: if ret: 
    ...:     print(ret.group()) 
    ...: else: 
    ...:     print("不在0-100之间") 
    ...:                                                                       
不在0-100之间

In [16]:  

# 改匹配第一个字符为[0-9],当然就可以匹配出08来了
In [9]: re.match('[0-9]?\d$','08').group()                                     
Out[9]: '08'

# 匹配100肯定报错,因为这里只是匹配两位字符,那么就需要使用 | 增加一个匹配的类型了。
In [17]: re.match('[1-9]?\d$','100').group()                                   
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-17-6c1fe61a1e0d> in <module>
----> 1 re.match('[1-9]?\d$','100').group()

AttributeError: 'NoneType' object has no attribute 'group'

In [18]:     

# 添加 | 用来后续可以多个一个判断 100 的情况
In [18]: re.match('[1-9]?\d$|100','100').group()                               
Out[18]: '100'

将括号中字符作为一个分组:(ab)

上面写到可以通过 | 来进行或条件匹配,但是却是没有限定范围。那是上面意思呢? 看看下面的这个例子来理解一下。

需求:匹配出163、126、qq邮箱

#coding=utf-8

import re

# 首先来简单匹配一个163的邮箱地址
In [19]: re.match('\w{4,20}@163\.com','test@163.com').group()                  
Out[19]: 'test@163.com'

# 那么这个要判断163、126、qq的邮箱,我是不是直接加上|就好了呢?从结果来看,并不是的。
In [20]: re.match('\w{4,20}@163|qq|126\.com','test@163.com').group()           
Out[20]: 'test@163'

In [21]: re.match('\w{4,20}@163|qq|126\.com','qq').group()                     
Out[21]: 'qq'

In [22]: re.match('\w{4,20}@163|qq|126\.com','126.com').group()                
Out[22]: '126.com'

In [23]:   
# 从上面的三个结果来看,貌似 | 把整体拆分三个规则来匹配。
# 很明显这不是我们想要的结果。明显就是 | 的或范围没有做好限制。
# 下面可以使用分组 () 来限定 或 的范围来解决问题

# 我在 (163|qq|126) 增加了括号,说明 | 这个或判断只在这个括号中有效果
In [23]: re.match('\w{4,20}@(163|qq|126)\.com','126.com').group()              
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-23-fd220fc6f021> in <module>
----> 1 re.match('\w{4,20}@(163|qq|126)\.com','126.com').group()

AttributeError: 'NoneType' object has no attribute 'group'

# 来看看,这个直接qq的当然就会匹配报错了。
In [24]: re.match('\w{4,20}@(163|qq|126)\.com','qq').group()                   
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-24-90ca396faa28> in <module>
----> 1 re.match('\w{4,20}@(163|qq|126)\.com','qq').group()

AttributeError: 'NoneType' object has no attribute 'group'

# 那么输入正确的邮箱地址,再来匹配看看是否正确。
In [25]: re.match('\w{4,20}@(163|qq|126)\.com','test@163.com').group()         
Out[25]: 'test@163.com'

In [26]: re.match('\w{4,20}@(163|qq|126)\.com','test@qq.com').group()          
Out[26]: 'test@qq.com'

In [27]: re.match('\w{4,20}@(163|qq|126)\.com','test@126.com').group()         
Out[27]: 'test@126.com'

In [28]:   
# 从上面的三个结果来看,都很正确匹配出来163、126、qq三种邮箱了。

# 最后输入另一种未定义的 hostmail 邮箱,当然就是报错的结果了。
In [28]: re.match('\w{4,20}@(163|qq|126)\.com','test@hostmail.com').group()    
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-28-a9b2cc3bf2e3> in <module>
----> 1 re.match('\w{4,20}@(163|qq|126)\.com','test@hostmail.com').group()

AttributeError: 'NoneType' object has no attribute 'group'

In [29]:     

需求:不是以4、7结尾的手机号码(11位)

In [29]: tels = ["13800001234", "18916844321", "10086", "18800007777"] 

In [35]: for tel in tels: 
    ...:     ret = re.match('^1\d{9}[0-35-68-9]$',tel) 
    ...:     if ret: 
    ...:         print("%s 手机号码结果不以4或者7结尾" % ret.group()) 
    ...:     else: 
    ...:         print("%s 手机号码不是想要找的" % tel) 
    ...:                                                                       
13800001234 手机号码不是想要找的
18916844321 手机号码结果不以4或者7结尾
10086 手机号码不是想要找的
18800007777 手机号码不是想要找的

In [36]:    

提取区号和电话号码

In [36]: re.match('\d{3,4}-?\d+','0755-12345678').group()                      
Out[36]: '0755-12345678'

In [37]: re.match('(\d{3,4})-?(\d+)','0755-12345678').group()                  
Out[37]: '0755-12345678'

In [38]: re.match('(\d{3,4})-?(\d+)','0755-12345678').group(1)                 
Out[38]: '0755'

In [39]: re.match('(\d{3,4})-?(\d+)','0755-12345678').group(2)                 
Out[39]: '12345678'

# 还有另外一种方式匹配,使用开头匹配符号 ^ 然后写上最后需要匹配的符号 - 
In [50]: re.match('[^-]*','0755-12345678').group()                             
Out[50]: '0755'

In [51]: re.match('[-]*','0755-12345678').group()                              
Out[51]: ''

In [52]: re.match('[^-]*','0755-12345678').group()                             
Out[52]: '0755'

In [53]: re.match('[^j]*','0755j12345678').group()                             
Out[53]: '0755'

In [54]: re.match('[^-]*','0755j12345678').group()                             
Out[54]: '0755j12345678'

In [55]: re.match('[^j]*','0755j12345678').group()                             
Out[55]: '0755'

In [56]:   

In [60]: re.match('[^-]*-?\d+','0755-12345678').group()                        
Out[60]: '0755-12345678'

In [61]: re.match('([^-]*)-?(\d+)','0755-12345678').group()                    
Out[61]: '0755-12345678'

In [62]: re.match('([^-]*)-?(\d+)','0755-12345678').group(1)                   
Out[62]: '0755'

In [63]: re.match('([^-]*)-?(\d+)','0755-12345678').group(2)                   
Out[63]: '12345678'

In [64]:       

# 这种写法的好处就是匹配所有 - 号前面的字符串
In [64]: re.match('([^-]*)-?(\d+)','Abcdasdasd-12345678').group(2)             
Out[64]: '12345678'

In [65]: re.match('([^-]*)-?(\d+)','Abcdasdasd-12345678').group(1)             
Out[65]: 'Abcdasdasd'

In [66]:  

引用分组num匹配到的字符串: \num

这个功能在做爬虫匹配网页HTML元素的时候经常会用到。 下面来看看示例:

需求:匹配出<html>hello beauty</html>

#coding=utf-8

import re

# 首先匹配第一个<html>看看
In [66]: re.match('<[a-zA-Z]*>','<html>hello beauty</html>').group()           
Out[66]: '<html>'

# 后面如果是用 .* 的话的确可以匹配所有字符,但是不利用后面想要再次() 分组
In [67]: re.match('<[a-zA-Z]*>.*','<html>hello beauty</html>').group()         
Out[67]: '<html>hello beauty</html>'

# 使用 \w 可以匹配字母、数字、下划线,但是不能匹配空格、tab等,所以只到hello这里。
In [68]: re.match('<[a-zA-Z]*>\w*','<html>hello beauty</html>').group()        
Out[68]: '<html>hello'

# 加上\s再两个\w之间进行匹配,那么就可以解决这个空格的问题了。剩下就是匹配最后的</html>
In [77]: re.match('<[a-zA-Z]*>\w*\s\w*','<html>hello beauty</html>').group()                 
Out[77]: '<html>hello beauty'

# 在最后写上匹配规则就可以了。
In [78]: re.match('<[a-zA-Z]*>\w*\s\w*</[a-zA-Z]*>','<html>hello beauty</html>').group()     
Out[78]: '<html>hello beauty</html>'

In [79]

# 但是,可以看到匹配的规则是大小写字母,如果不一样的html标签呢?
In [80]: re.match('<[a-zA-Z]*>\w*\s\w*</[a-zA-Z]*>','<html>hello beauty</h>').group()        
Out[80]: '<html>hello beauty</h>'

# 这样虽然也匹配出了结果,但是并不是想要的呀。最好的结果是结尾也应该是html
# 那么问题来了,很多时候匹配这是可能会变化的,不一定都是<html>,可能是<hello>都有可能。
In [81]:     

# 正确的理解思路:如果在第一对<>中是什么,按理说在后面的那对<>中就应该是什么

# 通过引用分组中匹配到的数据即可,但是要注意是元字符串,即类似 r""这种格式
In [89]: re.match(r'<([a-zA-Z]*)>\w*\s\w*</\1>','<html>hello beauty</html>').group()         
Out[89]: '<html>hello beauty</html>'

In [90]: re.match(r'<([a-zA-Z]*)>\w*\s\w*</\1>','<html>hello beauty</html>').group(1)        
Out[90]: 'html'

# 上面将匹配的内容结尾写成了 \1 ,那么就是直接使用第一个括号分组的内容。
In [91]:       

从上面可以看出,括号() 的分组在正则匹配是可以引用的,那么如果这种() 非常多,都写 \1 \2 \3 肯定不是很方便,那么下面有一种命名的编写方式。

分组别名引用:(?P<name>) (?P=name)

字符

功能

(?P<name>)

分组起别名

(?P=name)

引用别名为name分组匹配到的字符串

需求:匹配出<html><h1>www.baidu.com</h1></html>

#coding=utf-8

import re

In [92]: re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.baidu.com</h1></html>").group()                     
Out[92]: '<html><h1>www.baidu.com</h1></html>'

# 将第二个h标签改为 h2,使得不匹配h1。确认是否会报错。
In [94]: re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.baidu.com</h2></html>").group()                     
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-94-f0034ed73d2c> in <module>
----> 1 re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.baidu.com</h2></html>").group()

AttributeError: 'NoneType' object has no attribute 'group'

In [95]:    

不过这种方式知道就好,大部分也是使用 \1 \2 就可以完成匹配的了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

ffuf:Go语言编写的高速Web Fuzzer

ffuf是一款Go语言编写的高速Web Fuzzer工具,该项目深受大型项目gobuster和wfuzz的启发。

12720
来自专栏晓月寒·

少年郎,我这里有一份nginx配置,你拿走吧

worker_processes 4;:用来设置 Nginx 服务的进程数。该值推荐使用 CPU 内核数。

10920
来自专栏CWIKIUS

Angular 应用的外壳 原

你首先需要使用 Angular CLI 来创建一个初始化的应用。随后,你将对你已经初始化的应用进行修改来让你构建出 Tour of Heroes app(英雄指...

8610
来自专栏foochane

ipfs的基本操作

打开浏览器,输入:https://ipfs.io/ipfs/QmcPuRfoF5Joa4d9fxLP6CnrwBFgNCUFSorV438B8VqR7G

15420
来自专栏算法channel

Python读写csv文件专题教程(1)

Python的数据分析包Pandas具备读写csv文件的功能,read_csv 实现读入csv文件,to_csv写入到csv文件。每个函数的参数非常多,可以用来...

21920
来自专栏程序猿声

10分钟用Python爬取最近很火的复联4影评

《复仇者联盟4:终局之战》已经上映快三个星期了,全球票房破24亿美元,国内票房破40亿人民币。

13320
来自专栏晓月寒·

Spring Boot从入门到精通-页面模板

首先我们在resources目录下新建templates文件夹和static文件夹。 关于这两个文件夹,在Spring Boot中,静态资源默认是访问reso...

14210
来自专栏晓月寒·

nginx缓存静态资源,只需几个配置提升10倍页面加载速度

可以看到,静态资源占用了整个页面加载用时的90%以上,而且这个静态资源还是已经在我使用了nginx配置压缩以后的大小,如果没有对这些静态资源压缩的话,那么静态资...

1.2K30
来自专栏Linyb极客之路

SpringCloud认证和鉴权的6种方案

开始我们接触的时候权限认证 无从下手,但是当接触之后会发现 权限认证时一件很简单的事情,但是我们 方案众多又该如何选择呢,下面会分别对每种方案进行简单的阐述

1.2K30
来自专栏晓月寒·

vue实战-实现换主题/皮肤功能

接下来就是具体实现换皮肤功能了,换皮肤一般都是点击一个按钮弹出一些皮肤的选项,选中选项后皮肤生效。 我们将换皮肤功能抽成一个组件theme-switch。pc...

29820

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励