findall 函数返回所有匹配的子串,放在一个列表中。
content = '''
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满员
'''
import re
for one in re.findall(r'([\d.]+)万/每{0,1}月', content):
print(one)
元字符
. * + ? \ [ ] ^ $ { } | ( )
点-匹配所有字符
.
表示要匹配除了 换行符
之外的任何 单个
字符。
content1 = '''
小狗是小的
大狗是大的
黄狗是黄的
绿狗是绿的
'''
de = re.findall(r'.的',content1)
for i in de:
print(i)
星号-重复匹配任意次
*
表示匹配前面的子表达式任意次,包括0次。
content2 = '''
水啊,是冰凉的
火啊,是滚烫的
宇轩,不是我的
坤坤,是鸡叫的
'''
tt = re.findall(r',.*',content2)
for i in tt:
print(i)
加号-重复匹配多次
+
表示匹配前面的子表达式一次或多次,不包括0次。
content2 = '''
水啊,是冰凉的
火啊,是滚烫的
宇轩,不是我的
坤坤,
'''
tt = re.findall(r',.+',content2)
for i in tt:
print(i)
问号-匹配0-1次
?
表示匹配前面的子表达式0次或1次。
content2 = '''
水啊,寄冰凉的
火啊,你滚烫的
宇轩,太是我的
坤坤,美
'''
tt = re.findall(r',.?',content2)
for i in tt:
print(i)
花括号-匹配指定次数
花括号表示 前面的字符匹配 指定的次数
。
content2 = '''
水啊,寄冰凉的
火啊,你滚烫的
宇轩,太是我的
坤坤,美
'''
tt = re.findall(r',.{1}',content2)
for i in tt:
print(i)
贪婪模式和非贪婪模式
p1 = re.compile(r'<.*>')
p2 = re.compile(r'<.*?>')
print(p1.findall(source))
print(p2.findall(source))
造成结果存在差异的原因是p1使用了贪婪模式,即<.*>他会尽可能多地去把 匹配内容的情况。
而非贪婪模式则是点到为止。
对元字符的转义
content = '''苹果.是绿色的
橙子.是橙色的
香蕉.是黄色的'''
import re
p1 = re.compile(r'.*\.')
p2 = re.compile(r'.*.')
print(type(p1))
for one in p1.findall(content):
print(one)
for one in p2.findall(content):
print(one)
匹配某种字符类型
\d 匹配0-9之间任意一个数字字符,等价于表达式 [0-9]
\D 匹配任意一个不是0-9之间的数字字符,等价于表达式 [^0-9]
\s 匹配任意一个空白字符,包括 空格、tab、换行符等,等价于表达式 [\t\n\r\f\v]
\S 匹配任意一个非空白字符,等价于表达式 [^ \t\n\r\f\v]
\w 匹配任意一个文字字符,包括大小写字母、数字、下划线,等价于表达式 [a-zA-Z0-9_]
缺省情况也包括 Unicode文字字符,如果指定 ASCII 码标记,则只包括ASCII字母
\W 匹配任意一个非文字字符,等价于表达式 [^a-zA-Z0-9_]
方括号-匹配几个字符之一
方括号表示要匹配 指定的几个字符之一 。
比如
[abc]
可以匹配 a, b, 或者 c 里面的任意一个字符。等价于 [a-c]
。
[a-c]
中间的 - 表示一个范围从a 到 c。
如果你想匹配所有的小写字母,可以使用 [a-z]
一些 元字符 在 方括号内 失去了魔法, 变得和普通字符一样了。
比如
[akm.]
匹配 a k m .
里面任意一个字符
这里 .
在括号里面不在表示 匹配任意字符了,而就是表示匹配 .
这个 字符
如果在方括号中使用 ^
, 表示 非
方括号里面的字符集合。
content = 'a1b2c3d4e5'
p = re.compile(r'[^\d]' )
for one in p.findall(content):
print(one)
起始、结尾位置 和 单行、多行模式
^
表示匹配文本的 开头
位置。
正则表达式可以设定 单行模式
和 多行模式
如果是 单行模式
,表示匹配 整个文本
的开头位置。
如果是 多行模式
,表示匹配 文本每行
的开头位置。
多行模式
content = '''001-苹果价格-60
002-橙子价格-70
003-香蕉价格-80'''
p = re.compile(r'^\d+', re.M)
for one in p.findall(content):
print(one)
如果,去掉 compile 的第二个参数 re.M, 运行结果如下
$
表示匹配文本的 结尾
位置。
如果是 单行模式
,表示匹配 整个文本
的结尾位置。
如果是 多行模式
,表示匹配 文本每行
的结尾位置。
p = re.compile(r'\d+$',re.M)
for one in p.findall(content):
print(one)
竖线-匹配其中之一
特别要注意的是, 竖线在正则表达式的优先级是最低的, 这就意味着,竖线隔开的部分是一个整体
比如 绿色|橙
表示 要匹配是 绿色
或者 橙
,
而不是 绿色
或者 绿橙
content = '''苹果,是红色的
菠萝,是黄色的
小草,是绿色的'''
p = re.compile(r'黄色|红|绿',re.M)
for one in p.findall(content):
print(one)
括号-分组
括号称之为 正则表达式的 组选择。
组
就是把 正则表达式 匹配的内容 里面 其中的某些部分
标记为某个组。
content = '''苹果,苹果是绿色的
橙子,橙子是橙色的
香蕉,香蕉是黄色的'''
p = re.compile(r'(^.*),', re.MULTILINE)
for one in p.findall(content):
print(one)
p1 = re.compile(r'(^.*,)', re.MULTILINE)
for one in p1.findall(content):
print(one)
结果之所以不同是因为,结果是匹配到()当中的。若有多个()则返回把结果放入元组中
content = '''张三,手机号码15945678901
李四,手机号码13945677701
王二,手机号码13845666901'''
p = re.compile(r'^(.+),.+(\d{11})', re.MULTILINE)
for one in p.findall(content):
print(type(one))
print(one)
当有多个分组的时候,我们可以使用 (?P<分组名>...)
这样的格式,给每个分组命名。
这样做的好处是,更方便后续的代码提取每个分组里面的内容
p = re.compile(r'^(?P<name>.+),.+(?P<phone>\d{11})', re.MULTILINE)
for one in p.finditer(content):
print(one.group('name'),one.group('phone'))
re.finditer
和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回
让点匹配换行
众所周知,.(点)是不可以匹配换行符,但有些时候我们需要他完成这一项工作,因此我们需要DOTALL
参数
content = '''
<div class="el">
<p class="t1">
<span>
<a>Python开发工程师</a>
</span>
</p>
<span class="t2">南京</span>
<span class="t3">1.5-2万/月</span>
</div>
<div class="el">
<p class="t1">
<span>
<a>java开发工程师</a>
</span>
</p>
<span class="t2">苏州</span>
<span class="t3">1.5-2/月</span>
</div>
'''
# p = re.compile(r'class=\"t1\">.*<a>(.*)<',re.M)
p = re.compile(r'class=\"t1\">.*<a>(.*)</a',re.DOTALL)
for one in p.findall(content):
print(one)
切割字符串
字符串 对象的 split
方法只适用于 简单的字符串分割。 有时,你需要更加灵活的字符串切割。
names = '关羽; 张飞, 赵云, 马超, 黄忠 李逵'
namelist = re.split(r'[;,\s]\s*', names)
print(namelist)
正则表达式 [;,\s]\s*
指定了,分割符为 分号、逗号、空格 里面的任意一种均可,并且 该符号周围可以有不定数量的空格。
字符串替换
匹配模式替换
字符串 对象的 replace
方法只适应于 简单的 替换。 有时,你需要更加灵活的字符串替换。
# 在下面这段文本中 所有的 链接中 找到所以 /avxxxxxx/ 这种 以 /av 开头,
# 后面接一串数字, 这种模式的字符串。
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>
这节讲的是切割磁力线
'''
newStr = re.sub(r'/av\d+/', '/cn345677/' , names)
print(newStr)
sub 方法就是也是替换 字符串, 但是被替换的内容 用 正则表达式来表示 符合特征的所有字符串。
比如,这里就是第一个参数 /av\d+?/
这个正则表达式,表示以 /av
开头,后面是一串数字,再以 /
结尾的 这种特征的字符串 ,是需要被替换的。
第二个参数,这里 是 '/cn345677/'
这个字符串,表示用什么来替换。
第三个参数是 源字符串。
指定替换函数
# 替换函数,参数是 Match对象
def subFunc(match):
# Match对象 的 group(0) 返回的是整个匹配上的字符串,
src = match.group(0)
print(match.group(1))
# Match对象 的 group(1) 返回的是第一个group分组的内容
number = int(match.group(1)) + 6
dest = f'/av{number}/'
print(f'{src} 替换为 {dest}')
# 返回值就是最终替换的字符串
return dest
newStr = re.sub(r'/av(\d+?)/', subFunc , names)
print(newStr)
正则表达式 re.sub 函数执行时, 每发现一个
匹配的子串, 就会:
-
实例化一个 match对象
这个match 对象包含了这次匹配的信息, 比如:整个字符串是什么,匹配部分字符串是什么,里面的各个group分组 字符串是什么
-
调用执行 sub函数的第2个参数对象,也就是调用回调函数subFunc
并且把刚才产生的 match 对象作为参数传递给 subFunc