数据科学、数据分析、人工智能必备知识汇总-----Python爬虫-----持续更新:https://blog.csdn.net/grd_java/article/details/140574349 |
---|
文章目录
一、正则基础
1. 为什么使用正则
正则可以让我们方便的做字符串匹配,检验字符串格式,检索指定内容(子串)
- 例如,判断一个字符串是否是手机号等等
- 例如,提取文本中的所有ip地址
2. 正则与re模块简介
正则表达式,又称规则表达式
- 正则表达式(regular expression)描述了一种字符串匹配的模式(pattern)
- 正则匹配是一个
模糊的匹配
(不是精确匹配)
re模块
- python自1.5版本开始增加了re模块,该模块提供了perl风格的正则表达式模式
- 常用方法如下
- match()
- search()
- findall()
- finditer()
二、正则表达式
1. 匹配单个字符与数字
匹配 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符,当flags被设置为re.S时,可以匹配包含换行符在内的所有字符 |
[] | 里面是字符集合,匹配[里任意一个字符 |
[0123456789] | 匹配任意一个数字字符 |
[0-9] | 匹配任意一个数字字符 |
[a-z] | 匹配任意一个小写英文字母字符 |
[A-Z] | 匹配任意一个大写英文字母字符 |
[A-Za-z] | 匹配任意一个英文字母字符 |
[A-Za-z0-9] | 匹配任意一个数字或英文字母字符 |
[^lucky] | []里的^称为脱字符,表示非,匹配不在内的任意一个字符 |
^[lucky] | 以[]中内的某一个字符作为开头 |
\d | 匹配任意一个数字字符,相当于[0-9] |
\D | 匹配任意一个非数字字符,相当于[^0-9] |
\w | 匹配字母、下划线、数字中的任意一个字符,相当于[0-9A-Za-z_] |
\W | 匹配非字母、下划线、数字中的任意一个字符,相当于[^0-9A-za-z_] |
\s | 匹配空白符(空格、换页、换行、回车、制表),相当于[ \f\n\r\t];注意[]里面有空格哦 |
\S | 匹配非空白符(空格、换页、换行、回车、制表),相当于[^ \f\n\r\t] ;注意[]里面有空格哦 |
2. 限定符
符号 | 描述 | 示例 | 示例解析 |
---|---|---|---|
{num} | 按指定规则一次性匹配num个字符 | \d{3} | 按照\d 规则,匹配连续的3个字符,这3个字符全部满足\d 规则 |
? | 按照指定规则,匹配0或1个字符 | \d? | 按照\d 规则,匹配0或1个字符 |
* | 相当于{0,} 。按照指定规则,匹配任意个字符(0个,1个或多个) | \d* | 按照\d 规则匹配任意个 |
+ | 相当于{1,} 。按照指定规则,匹配1个或多个字符 | \W+ | 按照\W 规则,匹配非数字或字母字符,1个或多个 |
默认贪婪匹配,例如+代表匹配1个或多个,如果当前字符串符合条件的连续字符有多个,默认贪婪匹配多个,而不是1个1个匹配。
如何取消贪婪匹配,选择用非贪婪匹配呢?只需要限定符后面加个
?
即可。例如我们想要从java0123完成匹配
java\\d*
表示java开头,后面跟随0或多个数字。会直接匹配出java0123,因为是贪心匹配,优先尽可能多的匹配
python代码如下:看到了吗,python更方便,而java更特殊,几乎只有java的正则表达式是需要两个
\
的,python只需要一个
java\\d*?
表示java开头,后面跟随0或多个数字,并且非贪婪匹配。则会匹配出java,因为是非贪婪匹配,优先尽可能少的匹配,而*
表示匹配0或多个,最少就是0个,所以非贪婪优先最少,也就是0个
python代码
{num}:按照指定规则,连续num个字符匹配。就是至少连续num个符合指定规则的字符才能完成匹配 |
---|
使用例子"123abc1234abc123456789",我们要对其中123,1234,123456789进行匹配
- 直接用{num}形式,表示必须num个,不能多也不能少。下面例子中,我们按照1[0-9]规则并限定[0-9]{2}.也就是1开头,后面跟[0-9]数字,必须长度为2,不能多也不能少。
下图结果中可以看出,1开头满足后,只会匹配2个[0-9]的数字,多一个少一个都没有匹配到
python代码
- 用{num,}形式,表示至少匹配num个,上不封顶,我们[0-9]{num,}进行匹配时,会匹配[0-9]至少两个,多了也没关系,但是不能少
下图结果中可以看出,1开头满足后,后面匹配[0-9]至少2个,但是上不封顶
python
- 用{left,right}形式,至少匹配left个,最多匹配right个。默认贪婪匹配,如果一次性匹配不完(原字符串符合规则字符个数超出right,优先先匹配right个,剩下的再依次匹配)
下图中,我们限定开头必须是1,且对[0-9]限定至少2个,最多4个。匹配结果也同样满足条件
python
3. 定位符
规定要匹配的字符串出现的位置,比如在字符串开始还是结束位置。换句话说:指定要匹配的位置
符号 | 描述 | 示例 | 示例解析 |
---|---|---|---|
^ | 指定起始字符 | ^[0-9]+[a-z]* | 至少1个数字开头,后接任意个小写字母 |
$ | 指定结束字符 | ^[0-9]\-[a-z]+$ | 以一个数字开头,接连字符"-",至少1个小写字母结尾 |
\b | 匹配以指定规则结尾的边界 | yin\b* | 边界:用空格分开的子串的两边,或目标字符串的起始和结束位置,例如这个字符串“yinabcdyin aaaayin”,标黄的位置就是以yin结尾的边界 |
\B | 与\b 的含义正好相反,匹配以指定规则开头的边界 | yin\B | “yinabcdyin aaaayin”,其中标黄的是以yin为开头的边界 |
\A | 匹配字符串的开始,和^的区别是\A只匹配整个字符串的开头即使在re.M模式下也不会匹配每行的行尾 | ||
\Z | 匹配字符串的结尾,只匹配整个字符串的结尾 |
4. 选择匹配符
一个竖杠| 就是选择匹配符,和或运算符||有异曲同工之妙.表示只要满足一个条件就匹配 |
---|
例如我们想要实现匹配Java或者抓哇。两个随便一个都行,就可以通过选择匹配符来实现。下面的例子中,想要在字符串java抓哇中,匹配出java或者抓哇。那么就可以使用正则表达式java
|
抓哇
python
5. ()小括号的作用
- 和我们做数学题一样,起到一个"先算括号里面的东西"的作用
- 被小括号括起来的东西,会作为一个子存储被存储起来。和java底层实现差不多,可以参考一下:https://blog.csdn.net/grd_java/article/details/136120841
那么作为子存储匹配到的值,存储后,如何取出来呢?可以通过re模块常用函数group()进行
请参考下面《re模块中常用函数》中的《search函数》
三、re模块中常用函数
1. 通用flags(修正符)
值 | 说明 |
---|---|
re.I | 匹配时对大小写不敏感 |
re.M | 多行匹配,影响到^和$ |
re.S | 是. 匹配包括换行符在内的所有字符 |
如果要多个条件同时使用,通过
|
来拼接
'''导包(start)'''
import re
'''导包(end)'''
string = 'alksdjflksjal====192.168.0.1==186.725.111.565==kfjlkasjflkjdsladf'
pattern = ("(?P<one>[0-9]{1,3})."
"(?P<two>[0-9]{1,3})."
"(?P<three>[0-9]{1,3})."
"(?P<four>[0-9]{1,3})")
print(pattern)
matcher = re.findall(pattern, string,re.I|re.S|re.M)
print("matcher:",matcher)
2. 通用函数
获取匹配结果 |
---|
使用group()方法获取匹配到的值
groups()返回一个包含所有小组字符串的元组(也就是自存储的值),从1到 所含的小组号
3. match函数
从头匹配,只匹配一次。相当于search(“^”,string)
def match(pattern, string, flags=0):
'''
pattern: 匹配的正则表达式(一种字符串的模式)
string: 要匹配的字符串
flags: 标识位,用于控制正则表达式的匹配方式
'''
从字符串第一位开始匹配
,匹配成功返回匹配到的match对象。失败返回None。只匹配一次,就算字符串中有多个满足pattern条件的子串,也只返回第一个满足pattern的匹配可以理解为它只匹配字符串以什么开头。所以人如其名,这个函数主要用于匹配,检查。例如是否满足我们要求的秘密格式。
例如有一个字符串x123456,此时我们匹配[a-z],那么会匹配出x。因为字符串中第一个字符确实是x
但是如果字符串是1x23456,此时我们匹配[a-z],就会返回None。因为字符串第一个字符压根不是字母
4. search函数
子串搜索,只匹配一次
def search(pattern, string, flags=0):
'''
pattern: 匹配的正则表达式(一种字符串的模式)
string: 要匹配的字符串
flags: 标识位,用于控制正则表达式的匹配方式
'''
扫描整个字符串string,并返回第一个pattern模式成功的匹配,失败就返回None
只要字符串包含就可以,只匹配一次。和match要区分开,虽然他俩都只匹配一次。但是match必须从字符串第一个字符开始匹配,要是有一个对不上就失败。但search只要子串中匹配到就算成功。
也就是说,如果整个字符串有两个符合条件的子串,只会返回最先遇到的,匹配成功后,后面的字符串甚至直接省略不看了。
4.1 捕获分组
- (pattern):用一对小括号来实现分组称为非命名捕获。捕获匹配的子字符串。编号为零的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从1开始自动编号。
pattern是匹配规则,也就是要分组的正则表达式
- (?P< name>pattern):命名捕获。将匹配的子字符串捕获到一个组名称或编号名称中,用于name的字符串不能包含任何标点符合,并且不能以数字开头。
匹配字符串中的ip地址,并且为每一段地址分组,让我们可以方便的将每一段截取出来。因为search函数只匹配一次,所以字符串就算有多个ip地址,也只会匹配第一次遇到的
'''导包(start)'''
import re
'''导包(end)'''
string = 'alksdjflksjal====192.168.0.1==186.725.111.565==kfjlkasjflkjdsladf'
pattern = ("(?P<one>[0-9]{1,3})." +
"(?P<two>[0-9]{1,3})." +
"(?P<three>[0-9]{1,3})." +
"(?P<four>[0-9]{1,3})")
matcher = re.search(pattern, string)
print("matcher:",matcher)
print("matcher.group():",matcher.group())
print("matcher.groups():",matcher.groups())
print("matcher.group('one'):",matcher.group('one'))
print("matcher.group('two'):",matcher.group('two'))
print("matcher.group('three'):",matcher.group('three'))
print("matcher.group('four'):",matcher.group('four'))
不起名字的话,就只能用数字来获取指定个分组,例如第一个分组。
注意group(0),也就是第0组,就是整个匹配的串
'''导包(start)'''
import re
'''导包(end)'''
string = 'alksdjflksjal====192.168.0.1==186.725.111.565==kfjlkasjflkjdsladf'
pattern = ("([0-9]{1,3})." +
"([0-9]{1,3})." +
"([0-9]{1,3})." +
"([0-9]{1,3})")
matcher = re.search(pattern, string)
print("matcher:",matcher)
print("matcher.group():",matcher.group())
print("matcher.groups():",matcher.groups())
print("matcher.group(1):",matcher.group(1))
print("matcher.group(2):",matcher.group(2))
print("matcher.group(3):",matcher.group(3))
print("matcher.group(4):",matcher.group(4))
5. findall()函数(返回列表)
def findall(pattern, string, flags=0):
'''
pattern: 匹配的正则表达式(一种字符串的模式)
string: 要匹配的字符串
flags: 标识位,用于控制正则表达式的匹配方式
'''
扫描整个字符串string,返回所有匹配的pattern模式结果字符串列表
前面两个方法是找一次(找第一个匹配),这个是找多个,但是它会将所有匹配结果直接通过列表返回,而不是单独的match对象
6. finditer()函数(返回match对象迭代器)
finditer也是找多个,但是会返回一个迭代对象,每个匹配都会作为一个match对象返回
def finditer(pattern, string, flags=0):
'''
pattern: 匹配的正则表达式(一种字符串的模式)
string: 要匹配的字符串
flags: 标识位,用于控制正则表达式的匹配方式
'''
'''导包(start)'''
import re
'''导包(end)'''
string = 'alksdjflksjal====192.168.0.1==186.725.111.565==kfjlkasjflkjdsladf'
pattern = ("(?P<one>[0-9]{1,3})."
"(?P<two>[0-9]{1,3})."
"(?P<three>[0-9]{1,3})."
"(?P<four>[0-9]{1,3})")
print(pattern)
matcher = re.finditer(pattern, string)
print("matcher:",matcher)
for match in matcher:
print("================================================")
print(match)
print("match.group():",match.group())
print("match.groups():",match.groups())
print("match.group('one'):",match.group('one'))
print("match.group('two'):",match.group('two'))
print("match.group('three'):",match.group('three'))
print("match.group('four'):",match.group('four'))
print("================================================")
当然了,既然是迭代器,用next()函数也是可以的
next函数源码解释
def next(iterator, default=None): # real signature unknown; restored from __doc__
"""
next(iterator[, default])
返回迭代器中下一个条目
Return the next item from the iterator. If default is given and the iterator
is exhausted, it is returned instead of raising StopIteration.
"""
pass
则代码可以变成
'''导包(start)'''
import re
'''导包(end)'''
string = 'alksdjflksjal====192.168.0.1==186.725.111.565==kfjlkasjflkjdsladf'
pattern = ("(?P<one>[0-9]{1,3})."
"(?P<two>[0-9]{1,3})."
"(?P<three>[0-9]{1,3})."
"(?P<four>[0-9]{1,3})")
print(pattern)
matcher = re.finditer(pattern, string)
print("matcher:",matcher)
match1 = next(matcher)
print("match1:",match1)
match2 = next(matcher)
print("match2:",match2)
7. split()函数
切割字符串
def split(pattern, string, maxsplit=0, flags=0):
'''
pattern: 匹配的分隔符(依据什么进行分隔)
string: 要分割的字符串
maxsplit: 如果非0,则最多分隔出maxsplit个,剩下的会作为list中最后一个元素进行返回
flags: 标识位,用于控制正则表达式的匹配方式
'''
maxsplit指定为1,则会按指定分隔符分割一个元素出来,剩下的作为list最后一个元素返回
如果maxspit为默认值0,则会完全分割
'''导包(start)'''
import re
'''导包(end)'''
string = '010203,121314,222324;686967'
pattern = (",|;")
matcher = re.split(pattern=pattern,string=string,maxsplit=0,flags=0)
print("matcher:",matcher)
8. 编译
当在python中使用正则表达式时,re模块会做两件事,一件是编译正则表达式,如果表达式的字符串不合法,会报错。另一件是用编译好的正则表达式提取匹配字符串
如果一个正则表达式要使用几千遍,每一次都会编译,出于效率的考虑进行正则表达式的编译,就不需要每次使用都编译了,节省了编译的时间,从而提升效率
def compile(pattern, flags=0):
将pattern模式编译成正则对象
'''导包(start)'''
import re
'''导包(end)'''
string = '010203,121314,222324;686967'
pattern = (",|;")
compile = re.compile(pattern=pattern, flags=re.S)
print(type(compile))
# 保存为正则对象后,就可以用它调用匹配函数,进行使用了
print(compile.split(string))