正则表达式是处理字符串的强大的工具,它有着自己特点的语法结构。
对正则表达式没有了解的可以先网上自行了解一下,也可以看下我之前学习时的学习笔记正则表达式基础OTZ
:
匹配方法match()
首先介绍第一个匹配方法match(),通过向match()传入要匹配的字符串和正则表达式就可以检测这正则表达式是否能够匹配该字符串。
下面我们以'Hello I am Small_Fash, 21 years old, pick me!'
字符串进行操作。
match()方法会尝试从字符串的起始位置匹配正则表达式,如果匹配,就会返回匹配成功的结果;如果不匹配,就会返回None
import requests
import re
str = 'Hello I am Small_Fash, 21 years old, pick me!'
res = re.match('^Hello\s.*?\d{2}.*!',str)
print(res)
print(res.span(),res.group(),sep='\n') #span输出了匹配的返回
<re.Match object; span=(0, 45), match='Hello I am Small_Fash, 21 years old, pick me!'>
(0, 45)
Hello I am Small_Fash, 21 years old, pick me!
根据输出显示,成功匹配到了该字符串后可以通过group()方法打印出匹配到的字符串,span()输出了匹配的范围。
如果没有匹配将会返回None
,因为字符串开头不是Sello
import requests
import re
str = 'Hello I am Small_Fash, 21 years old, pick me!'
res = re.match('^Sello\s.*?\d{2}.*!',str)
print(res)
None
正则表达式开头的^
是匹配字符串的开头,也就是Hello开头;然后\s
匹配一个空白字符,用来匹配目标字符串的空格;接下来.
是匹配除换行符的任意字符,*
代表匹配0次或多次,?
在这里代表非贪婪方式匹配,\d
代表匹配任意数字,等价于[0-9],{2}
代表匹配前面的表达式2次(也就是\d);!代表匹配一个感叹号字符。
如果我们想要获得上面字符串的年龄信息21
呢?我们可以这样
import requests
import re
str = 'Hello I am Small_Fash, 21 years old, pick me!'
res = re.match('^Hello\s.*?(\d+).*!',str)
print(res)
print(res.span(),res.group(),res.group(1),sep='\n')
<re.Match object; span=(0, 45), match='Hello I am Small_Fash, 21 years old, pick me!'>
(0, 45)
Hello I am Small_Fash, 21 years old, pick me!
21
但是如果我们把?
去掉会怎么样呢?
import requests
import re
str = 'Hello I am Small_Fash, 21 years old, pick me!'
res = re.match('^Hello\s.*(\d+).*!',str)
print(res)
print(res.span(),res.group(),res.group(1),sep='\n')
<re.Match object; span=(0, 45), match='Hello I am Small_Fash, 21 years old, pick me!'>
(0, 45)
Hello I am Small_Fash, 21 years old, pick me!
1
年龄信息从21变成了1,这是因为.*
是贪婪的,它会尽可能的取多个匹配字符,因此将2给匹配了,而\d+
只能匹配剩下的1了。这点在字符串匹配中要格外注意。
修饰符
下面我们来匹配如下字符串:
str_another = '''Hello I am Sma
ll_Fash, 21 years old, pick me!'''
我们依然用上面的程序试一下
import requests
import re
str_another = '''Hello I am Sma
ll_Fash, 21 years old, pick me!'''
res = re.match('^Hello\s.*?(\d+).*!',str_another)
print(res)
print(res.span(),res.group(),res.group(1),sep='\n')
程序报错了!没有匹配到字符串!
None
Traceback (most recent call last):
File "C:\Users\86135\Desktop\test.py", line 11, in <module>
print(res.span(),res.group(),res.group(1),sep='\n')
AttributeError: 'NoneType' object has no attribute 'span'
这是因为.
匹配的是除换行符以外的任何字符,而待匹配字符串中有换行符,因此匹配失败,如果想避免这样的错误,需要添加第三个参数re.S
import requests
import re
str_another = '''Hello I am Sma
ll_Fash, 21 years old, pick me!'''
res = re.match('^Hello\s.*?(\d+).*!',str_another,re.S)
print(res)
print(res.span(),res.group(),res.group(1),sep='\n')
匹配成功~
<re.Match object; span=(0, 46), match='Hello I am Sma\nll_Fash, 21 years old, pick me!'>
(0, 46)
Hello I am Sma
ll_Fash, 21 years old, pick me!
21
re.S修饰符的作用是使.
匹配包括换行符在内的所有字符。
re.S在网页匹配中经常用到,因为HTML节点经常会有换行,加上它就可以匹配节点与节点之间的换行了。
其他修饰符如下:
修饰符 | 描述 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别匹配 |
re.M | 多行匹配,影响^和$ |
re.S | 使. 匹配包括换行符在内的所有字符 |
re.U | 根据Unicode字符解析字符 |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写的更易于理解 |
专业匹配
如果我们想匹配特殊字符如.
、?
等,在则需要用到转义字符\
,就可以匹配相应特殊字符了。
import requests
import re
str = 'Hello? I am Small_Fash.21 years old, pick me!'
res = re.match('^Hello\?\s.*?(\d+).*!',str)
print(res)
print(res.span(),res.group(),res.group(1),sep='\n')
匹配上了Hello后面的?
<re.Match object; span=(0, 45), match='Hello? I am Small_Fash.21 years old, pick me!'>
(0, 45)
Hello? I am Small_Fash.21 years old, pick me!
21
匹配方法search()
match()方法是从字符串开头开始匹配的,一旦开头不匹配,那么就会直接匹配失败。因此match()方法在使用时需要考虑开头的内容,很不方便OTZ
。
因此就有了search()方法,它在匹配时会扫描整个字符串,然后返回第一个成功匹配的结果,如果搜索完了还没有找到,则会返回None。
有如下一段待匹配的HTML文本,接下来我们就来对其进行信息提取:
str = '''<div class="subnav">
<ul class="navbar">
<li>
<a data-act="subnav-click" data-val="{subnavClick:7}"
href="/board/7"
>热映口碑榜</a>
</li>
<li>
<a data-act="subnav-click" data-val="{subnavClick:6}"
href="/board/6"
>最受期待榜</a>
</li>
</ul>
<ul class="newbar" >
<li>
<a data-act="subnav-click" data-val="{subnavClick:1}"
href="/board/1"
>国内票房榜</a>
</li>
<li>
<a data-act="subnav-click" data-val="{subnavClick:2}"
href="/board/2"
>北美票房榜</a>
</li>
<li>
<a data-act="subnav-click" data-val="{subnavClick:4}"
data-state-val="{subnavId:4}"
class="active" href="javascript:void(0);"
>TOP100榜</a>
</li>
</ul>
</div>'''
下面我们来尝试提取class
为newbar
的ul
节点内部的国内票房榜
res = re.search('<ul.*?newbar.*?<a.*?>(.*?)</a>',str,re.S)
print(res)
print(res.span(),res.group(1),sep='\n')
<re.Match object; span=(23, 427), match='<ul class="navbar">\n <li>\n <a data-act=>'
(23, 427)
国内票房榜
匹配方法findall()
前面的search()方法时返回匹配正则表达式的第一个内容,但是如果想要获取匹配正则表达式的所有内容,就需要使用findall()方法了。该方法会搜索整个字符串,然后匹配正则表达式的所有内容。
如果我们想要打印所有榜单,则可以像如下这样:
res = re.findall('<a.*?>(.*?)</a>',str,re.S)
print(res)
for r in res:
print(r)
['热映口碑榜', '最受期待榜', '国内票房榜', '北美票房榜', 'TOP100榜']
热映口碑榜
最受期待榜
国内票房榜
北美票房榜
TOP100榜
由上可知返回的是一个列表
匹配方法sub()
除了使用正则表达式提取信息外,有时候还需要借助它来修改文本。比如,想要把一串文本中的所有数字都去掉,如果只用字符串的replace()方法,就很繁琐,这时可以借助sub()方法。
import requests
import re
str = 'aldjf4as5f8s44wer884'
res = re.sub('\d+','',str)
print(res)
aldjfasfswer
有时候我们对字符串进行正则表达式匹配前可以先用sub()进行处理,然后再用search()、macth()、findall()等方法进行匹配。
compile()方法
前面所讲的方法都是处理字符串的方法,还有一个非常常用的方法是compile()方法,这个方法可以将字符串编译成正则表达式对象,以便在后面的匹配中也复用。
import re
content1 = '2016-12-15 12:00'
content2 = '2016-12-19 12:55'
content3 = '2018-12-15 18:32'
temp = re.compile('\d{2}:\d{2}')
res1 = re.sub(temp,'',content1)
res2 = re.sub(temp,'',content2)
res3 = re.sub(temp,'',content3)
print(res1,res2,res3,sep='\n')
如上有3个日期,我们想分别将3个日期中的时间去掉,这时可以借助sub()方法。该方法的第一个参数是正则表达式,但是这里没有必要重复写3个相同的正则表达式,可以借助compile()方法将正则表达式编译成一个正则表达式对象,以便复用。