正则表达式
基础知识
在线验证网址:www.regex101.com
-
提取的页面源代码本质上就是一个超长的字符串,可利用正则表达式准确提取内容
-
使用表达式的方式对字符串进行匹配的语法规则
-
优缺点
-
优点:速度快、效率高、准确性高
-
缺点:上手慢
-
-
语法格式:
- 使用元字符进行排列组合从而来匹配字符串
- 元字符:具有固定含义的特殊字符
-
作用
- 正则表达式是一种用于描述字符串模式的语言,可以用于匹配、查找、替换等操作。
-
常见元字符
元字符 | 解释 |
---|---|
.(贪婪) | 匹配除了换行符之外的任何单个字符,详见例1 |
*(贪婪) | 匹配前面的子表达式任意次,包括0次,详见例2 |
+(贪婪) | 匹配前面的子表达式任意次,注意:不包括0次,详见例3 |
{num1,num2} | 表示前面的字符匹配指定的次数(至少num1次,至多num2次),可用于提取电话号码等,详见例4 |
\ | 转义字符,作用:(1)将元字符变为普通字符,详见例5;(2)后接一些字符,表示匹配某种类型的一个字符 |
\d | 匹配任意一个0~9之间的数字字符,等价于表达式[0-9],详见例6 |
\D | 匹配任意一个不是0~9之间的数字字符,等价于表达式[^0-9],详见例7 |
\s | 匹配任意一个空白字符,包括空格、tab、换行符等,等价于表达式[\t\n\r\f\v] |
\S | 匹配任意一个非空白字符,等价于表达式[^\t\n\r\f\v] |
\w | 匹配任意一个文字字符,包括大小写字母、数字、下划线,等价于表达式[a-zA-Z0-9_],常见于判断用户名、密码是否规范,具体内容详见例8 |
\W | 匹配任意一个非文字字符,等价于表达式[^a-zA-Z0-9_] |
[ab] | 匹配方括号内a或b。例子:[\s,.]表示匹配任何空白字符或者逗号或者点,详见例9 |
[^] | 方括号中使用^,表示非的概念,即非方括号里面的字符集合,详见例10 |
^ | 表示匹配文本的起始位置,若是单行模式则表示匹配整个文本的开头位置;若是多行模式则表示匹配文本每行的开头位置,详见例11 |
$ | 表示匹配文本的结束位置,若是单行模式则表示匹配整个文本的结束位置;若是多行模式则表示匹配文本每行的结束位置,详见例12 |
() | 组选择,代表从正则表达匹配的内容里面扣取出其中的某些部分,详见例13 |
? | 表示匹配前面的子表达式0次或1次,详见例14 |
?P<name> | 此为命名捕获组语法,用来给给匹配的数据命名。详见例15 注意: 命名后提取数据的方式跟不命名不一样,详见例题 |
贪婪模式和非贪婪模式
点、星号、加号都是贪婪地,使用它们时,会尽可能多的匹配内容,如图所示
解决办法:
加上问号即可
re库
- 需要导入re库:
Python 的 re
(regular expression,正则表达式)库是用于处理正则表达式的标准库,它提供了一些函数和方法,用于对字符串进行模式匹配和查找。使用正则表达式可以方便地对字符串进行复杂的匹配和操作。
- re库中常用的函数和方法
函数和方法 | 解释 |
---|---|
re.match(pattern, string, flags=0) | 从字符串的开头开始匹配,如果字符串开头不符合正则表达式,返回 None;如果符合则返回一个MatchObject 对象 |
re.search(pattern, string, flags=0) | 在字符串中查找符合正则表达式的第一个子串,如果找到返回一个匹配的MatchObject 对象,否则返回 None。 |
rre.findall(pattern, string, flags=0) | 查找字符串中符合正则表达式的所有子串,并返回一个匹配对象的列表。注意:不是返回一个MatchObject 对象 |
str.replace(old, new,count) | 不是re库中的替换方法函数。old:原字符串;new:替换old的新字符串;count:为指定最多的替换次数,默认为-1,代表替换所有的匹配项 |
re.sub(pattern, repl, string, count=0, flags=0) | 在字符串中查找符合正则表达式的子串,并用指定的字符串替换它们。 |
re.split(pattern, string, maxsplit=0, flags=0) | 按照正则表达式的匹配结果将字符串分割成若干个子串,并返回一个列表。 |
re.compile(pattern, flags=0) | 编译正则表达式字符串,提前将其编译成一个正则表达式对象(预加载)。作用:(1)避免每次解析正则表达式字符串的开销并提高匹配效率(2)可对其利用re库中的各种方法进行处理 |
re.findall(pattern, string) | 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。 |
re.finditer(pattern, string) | 用于在字符串中查找所有匹配正则表达式的子串,并返回一个迭代器。每个迭代器元素都是一个MatchObject 对象,包含匹配的子串及其位置等信息。如图所示 |
若返回一个MatchObject
对象,则需要用obj.group()
来获取得到的结果
参数 | 解释 |
---|---|
pattern | 要匹配的正则表达式,可以是正则表达式字符串或正则表达式对象(若为正则表达式对象则可通过re.compile() 编译得到) |
string | 需要匹配的字符串 |
flags | 可选参数,用于指定正则表达式的标志位,例如“re.IGNORECASE”可用于忽略大小写"re.A"可用于在只选择英文文字字符时变更选择的编码(缺省为unicond编码,即可匹配任何字符);“re.MULTILINE”等同于"re.M"代表多行模式(缺省为单行模式) ;“re.S” 表示"点任意匹配模式",即"."可以匹配包括换行符在内的任意字符。 |
repl | 替换字符串,可以是一个字符串或一个函数 |
count | 可选参数,用于指定最多替换的次数。 |
maxsplit | 可选参数,用于指定最多分割的次数。 |
- 注意
(1)关于re.findall(pattern, string) 函数:
若未使用compile()函数将其字符串变为正则表达式对象,则你需要写pattern;反之则只需要引入string即可,详见例题1的两种方式
(2)在利用python进行爬虫时,re.finditer()方法和re.compile()方法的区别,用哪个更好?
re.compile()
方法用于将正则表达式编译为一个正则表达式对象(即预加载,提前把正则对象加载完毕),以便在后续的匹配中重复使用。而re.finditer()
方法用于在字符串中查找所有匹配正则表达式的子串,并返回一个迭代器。
如果需要在多个字符串中多次使用同一个正则表达式,那么使用re.compile()
方法可以提高效率。因为编译后的正则表达式对象可以被多次调用,而不需要重新解析正则表达式。而如果只需要在一个字符串中查找匹配的子串,那么使用re.finditer()
方法即可。
总之,以上两种方法均能提高效率!!!爬虫时可根据实际情况选择。
常见re库中方法的举例
re.finditer()方法
import re
result = re.finditer(r'\d+', "我今年18岁,我有2000万")
for i in result:
print(i)#返回迭代器
print(i.group())#从迭代器中返回匹配到的结果
re.search()方法
import re
result = re.search(r'\d+',"我叫周杰伦,我今年25岁了,我是3年纪2班的学生")
print(result)
#注意:re.search()方法返回的也是一个match对象,所以需要用group方法提取match对象中的数据
print(result.group())
re.match()方法
import re
result = re.match(r'\d+',"我叫周杰伦,我今年25岁了,我是3年纪2班的学生")
print(result)
print(result.group())
import re
result = re.match(r'\d+',"25岁的周杰伦,是3年纪2班的学生")
print(result)
print(result.group())
例题
01 .
例题1:从下面的例子中选择出所有的颜色
苹果是绿色的
橙子是橙色的
香蕉是黄色的
方式一
方式二
02 *
例题2:选择文本中逗号后的内容,包括逗号
注意:
.代表单个字符,然后在加上*代表任意个字符
03 +
例题3:选择文本中逗号后的内容,包括逗号
注意:.代表单个字符。最后一句话中的逗号后没有字符,所以不满足元字符+的条件
注意: 由于最后一句话中没有出现色字,不满足元字符+的条件,所以结果中没有黑
04 {}
例题4:{}可用于提取电话号码
05 \
例题5:返回所有点前面的字符串
06 \d
例题6:\d 匹配任意一个0~9之间的数字字符,等价于表达式[0-9]
07 \D
例题7:匹配任意一个不是0~9之间的数字字符,等价于表达式[^0-9]
08 \w
例题8:\w 匹配任意一个文字字符,包括大小写字母、数字、下划线,等价于表达式[a-zA-Z0-9_],常见于判断用户名、密码是否规范
注意:
若不做特殊设置,则会匹配中文、英文所有文字字符,如下:
若只想匹配英文文字字符,则需要在re.compile()函数的参数中加上参数flags,即re.A(作用:选择编码值)如下:
09 [ab]
例题9:[ab] 匹配方括号内a或b。例子:[\s,.]表示匹配任何空白字符或者逗号或者点
10 [^]
例题10:[^] 方括号中使用^,表示非的概念,即非方括号里面的字符集合
11 ^
例题11:^ 表示匹配文本的起始位置,若是单行模式则表示匹配整个文本的开头位置;若是多行模式则表示匹配文本每行的开头位置
单行模式
多行模式
12 $
例题12:$ 表示匹配文本的结束位置,若是单行模式则表示匹配整个文本的结束位置;若是多行模式则表示匹配文本每行的结束位置
单行模式
多行模式
13()
例题13:()组选择,代表从正则表达匹配的内容里面扣取出其中的某些部分
14 ?
例题14:? 表示匹配前面的子表达式0次或1次
15 ?P<name>
?P<name> 此为命名捕获组语法,用来给给匹配的数据命名
注意: 命名后,相当于将数据分为了不同的组别。此时就不能用findall()方法来得到数据,而是用finditer()方法来返回一个
MatchObject
对象数据,然后利用obj.group()方法来得到不同组别的数据
import re
a = '''
<div class = '西游记'><span id = '10010'>中国联通</span></div>
<div class = '西游记'><span id = '10086'>中国移动</span></div>
'''
#?P<name>
p = re.compile(r"<span id = '(?P<id>\d+)'>(?P<name>.+)</span>")
for i in p.finditer(a):
print(i.group("id") + ':' + i.group("name"))
# print(i.group("name"))
16 总训练
Python3 高级开发工程师 上海互教教育科技有限公司上海-浦东新区2万/月02-18满员
测试开发工程师(C++/python) 上海墨鹍数码科技有限公司上海-浦东新区2.5万/每月02-18未满员Python3 开发工程师 上海德拓信息技术股份有限公司上海-徐汇区1.3万/每月02-18剩余11人测试开发工程师(Python) 赫里普(上海)信息科技有限公司上海-浦东新区1.1万/每月02-18剩余5人Python高级开发工程师 上海行动教育科技股份有限公司上海-闵行区2.8万/月02-18剩余255人python开发工程师 上海优似腾软件开发有限公司上海-浦东新区2.5万/每月02-18满员
例题15 :从以上文本中提取出工资
切割字符串
字符串 对象的 split
方法只适用于 简单的字符串分割。 有时,你需要更加灵活的字符串切割。而正则表达式则适用于任何情况下的字符串分割
如下:
names = '关羽; 张飞, 赵云,马超, 黄忠 李逵'
若你想将人名切割出来,此时你仅仅利用split()方法就很困难,但是将其与正则表达式结合起来就很容易了
字符串替换
字符串 对象的 replace
方法只适应于 简单的 替换。 有时,你需要更加灵活的字符串替换
比如,我们需要在下面这段文本中 所有的 链接中 找到所以 /avxxxxxx/
这种 以 /av
开头,后面接一串数字, 这种模式的字符串。
然后,这些字符串全部 替换为 /cn345677/
。
names = '''
下面是这学期要学习的课程:
<a href='https://www.bilibili.com/video/av66771949/?p=1' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是牛顿第2运动定律
<a href='https://www.bilibili.com/video/av46349552/?p=125' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是毕达哥拉斯公式
<a href='https://www.bilibili.com/video/av90571967/?p=33' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是切割磁力线
'''
被替换的内容不是固定的,所以没法用 字符串的replace方法。
此时,可以使用正则表达式里面的 sub 方法
import re
names = '''
下面是这学期要学习的课程:
<a href='https://www.bilibili.com/video/av66771949/?p=1' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是牛顿第2运动定律
<a href='https://www.bilibili.com/video/av46349552/?p=125' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是毕达哥拉斯公式
<a href='https://www.bilibili.com/video/av90571967/?p=33' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是切割磁力线
'''
#match是一个类,match
def subFunc(match):
#match对象的group(0)代表返回的是整个匹配上的字符串
src = match.group(0)#等同于src = match[0](python3.6之后)
#match对象的group(1)代表返回的是第一个group分组的内容,也就是返回用第一个()括起来的内容
number = int(match.group(1)) + 6#等同于number = int(match[1]) + 6(python3.6之后)
#若有match.group(2)代表返回用第二个()括起来的内容,在本次代码中只有一个括号,即r'/av(\d+?)/'
dest = f'/av{number}/'#将字符串拼接起来
print(f'{src}替换为{dest}')
#返回值就是最终替换的字符串
return dest
'''注意:
在re.sub()方法中,第二个参数可以是一个函数,用于处理匹配到的每个子串,将其替换为指定的字符串。当使用函数作为第二个参数时,re.sub()
方法会将每个匹配到的子串作为参数传递给该函数,并将函数的返回值作为替换后的字符串。
在这个例子中,subFunc()函数被传递给了re.sub()方法作为第二个参数。当re.sub()方法找到匹配的子串时,会将该子串作为参数传递给subFunc()
函数,并将函数的返回值作为替换后的字符串。因此,subFunc()函数需要接受一个match对象作为参数,并返回一个字符串作为替换后的结果。
'''
newStr = re.sub(r'/av(\d+?)/', subFunc, names)
print(newStr)