正则表达式

本文详细介绍了Python中正则表达式的使用,包括match()、search()、findall()和sub()方法,以及贪婪与非贪婪匹配、修饰符和转义字符的概念。通过实例演示了如何从HTML文本中提取信息,并展示了如何利用编译后的正则表达式进行文本处理。同时,文章还提到了Python re模块的官方文档,供读者深入学习。
摘要由CSDN通过智能技术生成

https://tool.oschina.net/regex/ 正则表达式在线测试
官方文档:https://docs.python.org/zh-cn/3/library/re.html

1. match()方法

该方法主要传入两个参数,第一个参数是正则表达式,第二个参数是要匹配的字符串。

import re

content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^Hello\s(\d+)\sWorld', content)
print(result)
print(result.group())
print(result.group(1))
print(result.span())

运行结果:

<re.Match object; span=(0, 19), match='Hello 1234567 World'>
Hello 1234567 World
1234567
(0, 19)

在打印结果中,可以看到的结果是Match对象,这证明匹配成功,该对象有两个方法:group()方法可以输出匹配到的内容,结果是Hello 1234567 World。正好是匹配到的内容;span()方法输出匹配到的范围,结果是(0, 19),这就是匹配到的字符串在原来字符串中的位置范围。
这里想把字符串的1234567提取出来,此时可将数字部分的正则表达式用()括起来,然后调用group(1)获取匹配结果。
Group()输出完整的匹配结果。
Group(1)会输出第一个被()包围起来的匹配结果,加入正则表达式后面还有()括起来的内容,那依次使用group(2),group(3)来获取。

  • 贪婪与非贪婪匹配
    使用.*可以匹配任意字符。
import re

content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^He.*(\d+).*Demo$', content)
print(result)
print(result.group(1))

运行结果:

<re.Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
7

这里只匹配到一个7这个数字,为什么?
贪婪匹配与非贪婪匹配。在贪婪匹配下,.*会匹配尽可能多的字符。正则表达式中的.*后面是\d+,也就是至少一个数字,这里没有指定数字的个数,因此,前面的.*就尽可能匹配多的字符,这里就把123456匹配了,给\d+留下了一个可满足条件的数字7。

这里要使用非贪婪匹配,非贪婪匹配的写法:.*?多个一个问号。

import re

content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^He.*?(\d+).*Demo$', content)
print(result)
print(result.group(1))

运行结果:

<re.Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
1234567

此时成功获取到了1234567了。非贪婪匹配就是尽可能少的匹配字符。当.*?匹配到Hello 后面的空向字符时,再往后的字符就是数字了,而\d+恰好可以匹配,那么这里.的就不再进行匹配,交给\d+去匹配后面的数字。
注意:如果要匹配的字符串在结尾,.* ?就有可能什么都匹配不到,因为它匹配尽可能少的字符。

  • 修饰符
    正则表达式可以包含一些可选标志修饰符来控制匹配模式。修饰符被指定为一个可选的标志。
import re

content = '''Hello 1234567 World_This
is a Regex Demo'''
result = re.match('^He.*?(\d+).*Demo$', content)
print(result.group(1))

运行结果:

Traceback (most recent call last):
  File "E:/eclipse-workspace/xiaomi/111/re_test/re_test.py", line 30, in <module>
    print(result.group(1))
AttributeError: 'NoneType' object has no attribute 'group'

运行报错,也就是说正则表达式没有匹配到这个字符串,返回结果为None,而后面调用group(1)方法导致AttributeError

为什么加了一个换行符,就匹配不到呢?这是因为 . 匹配的是除了换行符之外的所有字符,当遇到了换行符,.*?就不能匹配了。这里需要加上一个修饰符re.S,即可修正这个错误:

content = '''Hello 1234567 World_This
is a Regex Demo'''
result = re.match('^He.*?(\d+).*Demo$', content, re.S)

运行结果:

1234567

这个修饰符re.S的作用是使 . 匹配包括换行符在内的所有字符。
这个re.S 在网页匹配中经常用到。因为HTML 节点经常会有换行,加上它,就可以匹配节点与节点之间的换行了。
还有一些修饰符re.I,意思是对大小写不敏感。这个在网页中也比较常用。

  • 转义
    使用\进行转义
import re

content = '(百度)www.baidu.com'
result = re.match('\(百度\)www\.baidu\.com', content)
print(result)

结果如下:

<re.Match object; span=(0, 17), match='(百度)www.baidu.com'>

当遇到用于正则匹配模式的特殊字符时,在前面加反斜杠转义一下。如使用.来匹配小数点;用()来匹配()

2.search()

前面是match()方法是从字符串开头开始匹配的,一旦开头不匹配,那么整个匹配都会失败。

import re

content = 'Extra stings Hello 1234567 World_This is a Regex Demo Extra stings'
result = re.match('Hello.*?(\d+).*?Demo',content)
print(result)

运行结果:

None

使用match()方法失败。
Search()方法它在匹配时会扫描整个字符串,然后返回第一个匹配成功的结果。将上面的代码中match()改为search(),运行结果:

<re.Match object; span=(13, 53), match='Hello 1234567 World_This is a Regex Demo'>

下面用几个实例看看search()的用法
首先,有段带匹配的html文本,我们写几个正则表达式实例来实现相应信息的提取

'''<div id="songs-list" >
<h2 class=”title ”>经典老歌</h2>
<p class=” introduction ”>
经典老歌列表
</p>
<ul id="list" class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href ="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4" class="active">
<a href ="/3.mp3" singer="齐秦">往事随风</a>
</li>
<li data-view ="6"><a href="/4.mp3" singer="beyond">光辉岁月</a><lli>
<li data-view="5">< a href="/5.mp3" singer="陈慧琳">记事本</a><lli>
<li data-view="5">
<a href ="/6.mp3" singer="邓丽君">但愿人长久</a>
</li>
</ul>
</div>'''

可以观察到, ul 节点里有许多li 节点,其中li 节点中有的包含a节点,有的不包含a节点, a节点还有一些相应的属性一一超链接和歌手名。
尝试提取class 为active 的li 节点内部的超链接包含的歌手名和歌名,此时需要提取第三个li 节点下a 节点的singer 属性和文本。
此时的正则表达式可以是以li开头,然后寻找标志符active,中间的部分可以用 .?来匹配,要提取singer这个属性值,所以还要写singer=”(.?)”,这里使用小括号,以便后面使用group()方法提取出来。还要匹配a文本的内容,它的左边界是>,右边界是,依然使用(.*?)来匹配,所以最后的正则表达式为:

<li.*?active.*?singer=(.*?)>(.*?)</a>

然后调用search()方法,它会搜索整个HTML文本,找到符合正则表达式的第一个内容返回。
另外,由于HTML文本中的代码有换行,所以要传入第三个参数re.S

import re

html = '''<div id="songs-list" >
<h2 class=”title ”>经典老歌</h2>
<p class=” introduction ”>
经典老歌列表
</p>
<ul id="list" class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href ="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4" class="active">
<a href ="/3.mp3" singer="齐秦">往事随风</a>
</li>
<li data-view ="6"><a href="/4.mp3" singer="beyond">光辉岁月</a><lli>
<li data-view="5">< a href="/5.mp3" singer="陈慧琳">记事本</a><lli>
<li data-view="5">
<a href ="/6.mp3" singer="邓丽君">但愿人长久</a>
</li>
</ul>
</div>'''

result = re.search('<li.*?active.*?singer="(.*?)">(.*?)</a>', html, re.S)
if result:
    print(result.group(1),result.group(2))

歌手和歌名都是用小括号,使用group(1)和group(2)进行输出。

齐秦 往事随风

如果正则表达式中不加active,结果会如何呢?

result = re.search('<li.*?singer="(.*?)">(.*?)</a>', html, re.S)
If  result:
print(result.group(1),result.group(2))

结果是:

任贤齐 沧海一声笑

如果去掉re.S呢?

result = re.search('<li.*?singer="(.*?)">(.*?)</a>', html)
if result:
    print(result.group(1),result.group(2))

结果是:

result = re.search('<li.*?singer="(.*?)">(.*?)</a>', html)
if result:
    print(result.group(1),result.group(2))

3.findall()
如果想要获取匹配正则表达式的所有内容,就需要借助findall()方法了,该方法会搜索整个字符串,然后返回匹配正则表达式的所有内容。
还是上面的HTML文本,如果想获取a节点的超链接、歌手和歌名,就将search()换为findall()方法,如果有返回的话,就是列表类型,所以要遍历一下一次获取每组的内容。

results = re.findall('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>', html, re.S)
print(results)
print(type(results))
for result in results:
    print(result)
    print(result[0], result[1], result[2])

运行结果:

[('/2.mp3', '任贤齐', '沧海一声笑'), ('/3.mp3', '齐秦', '往事随风'), ('/4.mp3', 'beyond', '光辉岁月'), ('/5.mp3', '陈慧琳', '记事本'), ('/6.mp3', '邓丽君', '但愿人长久')]
<class 'list'>
('/2.mp3', '任贤齐', '沧海一声笑')
/2.mp3 任贤齐 沧海一声笑
('/3.mp3', '齐秦', '往事随风')
/3.mp3 齐秦 往事随风
('/4.mp3', 'beyond', '光辉岁月')
/4.mp3 beyond 光辉岁月
('/5.mp3', '陈慧琳', '记事本')
/5.mp3 陈慧琳 记事本
('/6.mp3', '邓丽君', '但愿人长久')
/6.mp3 邓丽君 但愿人长久

4.sub()

除了使用正则表达式提取信息外,有时候还需要它来就该文本。如:想要把一串文本中的所有数字都去掉,如果只用字符串的replace()方法,那就太繁琐了,使用sub方法。

import re
content = '43gfn7gfs8hclf54njk392sdf'
content = re.sub('\d+', '',content)
print(content)

运行结果:

Gfngfshclfnjksdf

该方法实际上是做了替换,将匹配到的数字替换为空字符,就等于去掉了数字。

在上面html的示例中,如果只想取出歌名,就可以使用sub()方法将a节点去掉,只留下文本,在使用findall()提取就好了

html = re.sub('<a.*?>|</a>', '', html)
print(html)
resluts = re.findall('<li.*?>(.*?)</li>', html, re.S)
for reslut in resluts:
    print(reslut.strip())

运行结果:

<div id="songs-list" >
<h2 class=”title ”>经典老歌</h2>
<p class=” introduction ”>
经典老歌列表
</p>
<ul id="list" class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
沧海一声笑
</li>
<li data-view="4" class="active">
往事随风
</li>
<li data-view ="6">光辉岁月</li>
<li data-view="5">记事本</li>
<li data-view="5">
但愿人长久
</li>
</ul>
</div>
一路上有你
沧海一声笑
往事随风
光辉岁月
记事本
但愿人长久

html = re.sub(’<a.*?>|’, ‘’, html) 这里的 | 表示并列匹配。
<a href="/2.mp3" singer=“任贤齐”>沧海一声笑</a> 将匹配到的替换为空,只剩下了歌名。

5.Compile()

这个方法可以将正则字符串编译成正则表达式对象,以便在后期的匹配中使用。

import re

content1 = '2021-3-30 12:00'
content2 = '2021-3-28 12:55'
content3 = '2021-3-26 13:15'
pattern = re.compile('\d{2}:\d{2}')
result1 = re.sub(pattern, '', content1)
result2 = re.sub(pattern, '', content2)
result3 = re.sub(pattern, '', content3)
print(result1,result2,result3)

运行结果:

2021-3-30  2021-3-28  2021-3-26 

Compile()还可以传入修饰符,例如:re.S等修饰符,这样在search()、findall()等方法就不需要再次传入了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值