最近在学习爬虫,发现有时爬取出来的数据十分杂乱,如果想要寻找指定要求字符串发现用正则表达式十分方便,慢慢开始了解正则表达式,发现正则表达式真的很强大,用这篇博客作为笔记学习正则表达式
一、正则表达式概念及简介
1.正则表达式:
正则表达式是对字符串的一种逻辑公式,用事先定义好的一些特定字符、以及这些字符的组合,组成一个“规则字符串”,这个字符串能用来表达对字符串的一种过滤逻辑
2.简介:
正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本,使用单个字符串来描述与匹配一系列符合某个句子规则的字符串
二、为什么使用正则表达式(正则表达式的优点)
由于正则表达式能做的都可以通过我们的编程来实现,那么我们学习正则的原因也就是正则的优点:
- 正则表达式能够很大幅度的简化代码,用起来也蛮顺手的,例如平常需要写20+行数的代码,正则表达式一句话可能就会写出。所以说对于刚接触的人可能会有一些晦涩难懂。
- 由于正则表达式事先定义好一些特定字符以及组合,所以在用正则表达式处理字符串的话代码更容易理解。
- 由于正则表达式简化代码的能力,所以正则表达式的速度可能远比自己写的逻辑要高一些。
三、Python中如何使用正则表达式
1.如果想在Python中使用正则表达式,就需要去了解Python的re模块,re模块使Python拥有全部的正则表达式功能
2.正则表达式通常分为贪婪模式和非贪婪模式,Python默认是贪婪模式
- 贪婪模式:总是尝试匹配尽可能多的字符
- 非贪婪模式:总是尝试匹配尽可能少的字符
- 举例:如果在Python中想要匹配字符“qwer”,需要代码“qwer+”,由于采用的是贪婪模式,所以他会一直向后匹配,一直到最后一个“r”的时候结束,返回匹配结果“qwerrrrr”。如果想使用非贪婪模式则需要把代码改为“qwer+?”,当匹配到“qwer”的时候,已经匹配成功则直接结束
四、正式学习正则表达式
1.元字符
(1)字符处理
元字符 | 解释 | 例子 | 结果 |
---|---|---|---|
. | 匹配任意字符(除去换行符号) | .abc | abc |
\ | 转义字符,是后一个字符变成原来的意思 | a\\b | a\b |
[…] | 字符集,对应位置逐个列出,可以给出范围,例如[abc]或[a-c] | a[bcd]e | abe、ace、ade |
(2)数量词
元字符 | 解释 | 例子 | 结果 |
---|---|---|---|
* | 匹配前一个子表达式0次或多次 | abc* | ab、abc、abccc |
+ | 匹配一个字符串一次或者无限次 | abc+ | abc、abccc、abcccccc |
? | 匹配一个字符串一次或者零次 | abc? | ab、abc |
{m} | 匹配一个字符m次 | abc{2} | abcc |
{m,n} | 匹配一个字符串m到n次 | abc{3,2} | abcc、abccc |
(3)边界匹配
元字符 | 解释 | 例子 | 结果 |
---|---|---|---|
^ | 匹配开始位置,多行模式下匹配每一行的开始 | ^abc | abc |
$ | 匹配结束位置,多行模式下匹配每一行的结束 | abc$ | abc |
\A | 匹配字符串开始位置,忽略多行模式 | \Aabc | abc |
\Z | 匹配字符串结束位置,忽略多行模式,如果存在换行,匹配到换行前的结束字符串 | abc\Z | abc |
\b | 匹配一个单词边界,也就是指单词和空格键的位置。例如匹配never中的er,"er\b"可以匹配“never”中的“er”,但不能匹配“verb”中的“er” | ||
\B | 匹配非单词边界,也就是[^\b],可以匹配“verb”中的“er” |
(4)预定义字符集
元字符 | 解释 | 例子 | 结果 |
---|---|---|---|
\d | 匹配任一十进制数:0-9 | a\dc | a1c |
\D | 匹配任一非十进制数=[^0-9] | a\Dc | abc |
\s | 空白字符 | a\sc | a c |
\S | 非空白字符[^\s] | a\Sc | abc |
\w | 匹配数字、字母、下划线中任一个字符[a-zA-Z0-9_] | a\wc | abc |
\W | [^\w] | a\Wc | a c |
(5)逻辑与分组
元字符 | 解释 | 例子 | 结果 |
---|---|---|---|
| | 逻辑”或”运算 | a|b | a、b |
(…) | 分组,将两个括号中间的表达式定义为组,并且将当前匹配到的表达式字符表存在一个临时区域 | (abc){2} | abcabc |
(?P<name>…) | 分组的命名模式,取这个组内容可以使用索引,也可以使用name,同一正则表达式中,每个grouo的组名是唯一的不能,不能重复 | ||
(?P=name) | 对应上一个,这个是分组的引用形式,也就是说可以引用前面命名过的正则表达式 | ||
\number | 匹配和前面索引为number的分组捕获到内容一样的字符串 |
(6)特殊构造
元字符 | 解释 | 例子 | 结果 |
---|---|---|---|
(?#…) | 把#后的字符当做注释忽略 | abc(#asdfasd)edf | abcedf |
(?=…) | 在表达式‘…’之前的字符串 | 在字符串‘ddpython’中(?=python) | 会匹配‘dd’ |
(?!..) | 在后面不跟表达式‘…’的字符串 | 如果‘dd’后面不是字符串‘python’的话(?=python) | 匹配’dd’ |
(?<=…) | 当前位置左侧是否能够匹配“…”,需要匹配才会成功 | 正则表达式’(?<=abc)def’,会在‘abcdef’中匹配‘def’ | |
(?<!..) | 与上一条呈相反状态 | ||
(?: ) | 取消有限打印分组内容 | 在abc中‘(?:a)(b)’ | [b] |
?P<> | 指定key | 在abc中(?P<n1>a) | groupdict{n1:a} |
(?(id/name)yes|no) | 对于给出的id或者name,先尝试匹配yes部分。如果id或者name不满足,则去匹配no部分内容 |
2.re模块主要方法
(1)首先让我们先介绍一下正则表达式中的参数flag:
- I:ignorecase,忽略大小写的匹配模式
- L:locale,使本地化识别(locale-aware)匹配,也就是字符本地化(使预定字符类\w、\W、\b、\B、\s、\S取决于当前区域设定)
- M:multiline,多行模式,将字符串视为多行,从^匹配每一行的行首,$匹配每一行的行位
- S:dotall,此模式下‘ . ’的匹配不受限制,可以匹配任何字符,包括换行符
- X:verbose,详细模式,此模式忽略正则表达式中的空白和#号注释,并且表达式可以是多行
- U:unicode,使预定字符(\w、\W、\b、\B、\s、\S、\d、\D)取决于当前区域设定
(2)
①compile(pattern[,flag]):对正则表达式pattern进行编译,编译后比直接查找速度快,flag是匹配模式,而且取值可以使用按位或‘|’表示也会生效,比如re.I | re.M
另外可以在regex字符串中指定模式,比如re.compile(‘pattern’,re.I | re.M)与re.compile(’(?im)pattern’)是等价的
例子:
import re
text = "lengxiang is a good boy, he is very cool!"
result = re.compile(r'\w*oo\w*')
print(result.findall(text))
____________________________________________________________________
output>>['good','cool']
②match(patter,string[,flag]):从字符串string的开始就匹配,若匹配成功,则返回匹配对象,否者返回None
re.match对象有以下方法:
import re
a = "abc123def"
find_job = re.match("abc",a);
print(find_job)
print(find_job.group())#返回被re匹配的字符串
print(find_job.start())#返回匹配开始的位置
print(find_job.end())#返回匹配结束的位置
print(find_job.span())#返回一个元组包含匹配(开始,结束)位置
____________________________________________________________________
output>>
<re.Match object; span=(0,3),match='abc'>
abc
0
3
(0,3)
③re.search(pattern,string[,flag]):该方法与match方法类似,re.match只匹配字符串的开始,如果字符串开始不匹配就直接None,而re.search匹配整个字符串,直到找到能够匹配的。
import re
a = "abc123def"
find_job = re.search("abc",a);
print(find_job)
print(find_job.group())
print(find_job.start())
print(find_job.end())
print(find_job.span())
____________________________________________________________________
output>>
<re.Search object; span=(3,6),search='123'>
123
3
6
(3,6)
④re.split(pattern,string[,maxspilt,flag]):能匹配的子串将string分割后返回列表,maxspilt:分隔次数,如果分隔次数不指定的话将全部分隔。
import re
splitIng = "k ad f2n git bn3gw"
print(re.split(r'\s+|\d+|[,]',splitIng))
____________________________________________________________________
output>>
['k','ad','f','n','git','bn','gw']
⑤re.findall(pattern,string[,flag]):返回string中所有与pattern匹配的字符串 ,返回的是数组形式。
import re
findallIng = "as1dfg3hjk2l"
result = re.compile(r'\D+')
print(re.findall(result,findallIng))
____________________________________________________________________
output>>
['as','dfg','hjk','l']
⑥re.finditer(pattern,string[,flag]):返回一个产生匹配对象实体的迭代器,能产生字符串中所有re模式串的非重叠匹配
import re
content = '''email:123456@163.com
email:1668362496@qq.com
email:1234567890@163.com
'''
pattern = re.compile(r'(?P<number>\d+)@(?P<mail_type\w+>).com)
result = re.finditer(pattern,content)
iter_dict = {}
for i in result:
print("邮箱号码:",i.group(1),"类型:",i.group(2))
number = i.group(1)
mail_type = i.group(2)
iter_dict.setdefault(number,mail_type)
print(irer_dict)
____________________________________________________________________
output>>
邮箱号码:123456 类型:163
邮箱号码:1668362496 类型:qq
邮箱号码:1234567890 类型:163
{'123456':'163','1668362496':'qq','1234567890':'163'}
未完待续…