1.正则表达式
Regular Expression,正则表达式
用来描述匹配规则的表达式,针对字符串进行比较和匹配
作用:用来检索、替换哪些符合某个规则的文本
使用场景:
- 验证数据 前端,电话/邮箱
- 抓数据 页面抓取数据
- 洗数据 洗掉冗余数据
1.1Python 里的正则表达式(re 模块)
match(匹配规则,被匹配的字符串)方法==从头==开始匹配,匹配成功返回 match 对象,匹配失败返回 None(只匹配一次)- 从 match 对象获取字符串的方法为
.group() - 获取索引位置的元组
span()、起始位置start()、结束位置end()
import re
s = "我喜欢吃火锅"
ret = re.match("我喜欢",s)
print(ret)
print(ret.group())
print(ret.span())
print(ret.start())
print(ret.end())
out:
<_sre.SRE_Match object; span=(0, 3), match='我喜欢'>
我喜欢
(0, 3)
0
3
search(匹配规则,被匹配的字符串)从字符串中进行搜索,一旦匹配成功返回第一次匹配成功的位置,不成功返回 None,使用方法和 match 一样(只匹配一次)
import re
s = "我喜欢吃火锅我喜欢吃火锅"
ret1 = re.search("火锅",s)
print(ret1)
print(ret1.group())
out:
<_sre.SRE_Match object; span=(4, 6), match='火锅'>
火锅
-
findall(匹配规则,被匹配的字符串),搜索全部,匹配全部,返回一个列表,包含所有符合规则的字符串。 -
finditer(匹配规则,被匹配的字符串),搜索全部,匹配全部,返回一个迭代器,对象是 match 对象。
import re
s = "我喜欢吃火锅我喜欢吃火锅"
ret1 = re.findall("火锅", s)
print(ret1)
ret2 = re.finditer("火锅", s)
for i in ret2:
print(i)
out:
['火锅', '火锅']
<_sre.SRE_Match object; span=(4, 6), match='火锅'>
<_sre.SRE_Match object; span=(10, 12), match='火锅'>
split()将字符串按照匹配规则进行切割,返回一个切割后的列表
import re
s = "asdf jkl; asdf werq,asdf, foo"
# 先匹配一个空格,后面可能有任意个空格
ret = re.split("[\s,;]\s*", s)
print(ret)
out:
['asdf', 'jkl', 'asdf', 'werq', 'asdf', 'foo']
sbu(规则,替换的串,被匹配的字符串)用来做高级替换
import re
s = "我还要再活500年,20年"
ret = re.sub("\d+", "1000", s)
print(ret)
ret = re.sub("\d+", "2000", s)
print(ret)
out:
我还要再活1000年,1000年
我还要再活2000年,2000年
sub()还有高阶用法,传入函数,对匹配到的 Match 对象进行操作,并返回一个字符串
import re
s = "我还要再活500年,20年"
def func(num):
# 匹配到的是一个Match对象,所以传进来是个Match对象,要使用group获取字符串
n = num.group()
return str(int(n) + 1)
ret = re.sub("\d+", func, s)
print(ret)
ret = re.sub("\d+", func, s)
print(ret)
out:
我还要再活501年,21年
我还要再活501年,21年
1.2 正则表达式的语法
1.2.1 单字符匹配
| 方法 | 含义 |
|---|---|
| . | 匹配单字符(万能匹配,什么都可以匹配),但是不能匹配\n |
| \d | 匹配所有的数字(0-9 的单字符) |
| \D | 匹配所有的非数字(0-9 的单字符) |
| \w | 匹配所有的单词字符(数字、字母和下划线),Python3 中汉字也是单词字符 |
| \W | 上述相反 |
| \s | 匹配所有空白符 |
| \S | 匹配所有非空白符 |
| [] | 内部任意一个(^表示非) |
案例:
ret = re.match("张.龙", "张海龙")
print(ret)
ret = re.match("张.龙", "张龙")
print(ret)
ret = re.match("张.龙", "张\n龙")
print(ret)
out:
<_sre.SRE_Match object; span=(0, 3), match='张海龙'>
None
None
ret = re.match("张.龙", "张2龙")
print(ret)
ret = re.match("张\d龙", "张2龙")
print(ret)
ret = re.match("张\d龙", "张12龙")
print(ret)
ret = re.match("张\d龙", "张海龙")
print(ret)
out:
<_sre.SRE_Match object; span=(0, 3), match='张2龙'>
<_sre.SRE_Match object; span=(0, 3), match='张2龙'>
None
None
ret = re.match("张\D龙", "张2龙")
print(ret)
ret = re.match("张\D龙", "张s龙")
print(ret)
ret = re.match("张\D龙", "张#龙")
print(ret)
ret = re.match("张\D龙", "张海龙")
print(ret)
out:
None
<_sre.SRE_Match object; span=(0, 3), match='张s龙'>
<_sre.SRE_Match object; span=(0, 3), match='张#龙'>
<_sre.SRE_Match object; span=(0, 3), match='张海龙'>
ret = re.match("张\w龙", "张海龙")
print(ret)
ret = re.match("张\w龙", "张s龙")
print(ret)
ret = re.match("张\w龙", "张#龙")
print(ret)
ret = re.match("张\w龙", "张2龙")
print(ret)
out:
<_sre.SRE_Match object; span=(0, 3), match='张海龙'>
<_sre.SRE_Match object; span=(0, 3), match='张s龙'>
None
<_sre.SRE_Match object; span=(0, 3), match='张2龙'>
ret = re.match("张\W龙", "张海龙")
print(ret)
ret = re.match("张\W龙", "张s龙")
print(ret)
ret = re.match("张\W龙", "张#龙")
print(ret)
ret = re.match("张\W龙", "张2龙")
print(ret)
out:
None
None
<_sre.SRE_Match object; span=(0, 3), match='张#龙'>
None
ret = re.match("张\s龙", "张 龙")
print(ret)
ret = re.match("张\s龙", "张\n龙")
print(ret)
ret = re.match("张\s龙", "张\r龙")
print(ret)
ret = re.match("张\s龙", "张\t龙")
print(ret)
ret = re.match("张\s龙", "张\f龙")
print(ret)
ret = re.match("张\s龙", "张海龙")
print(ret)
ret = re.match("张\s龙", "张龙")
print(ret)
out:
<_sre.SRE_Match object; span=(0, 3), match='张 龙'>
<_sre.SRE_Match object; span=(0, 3), match='张\n龙'>
<_sre.SRE_Match object; span=(0, 3), match='张\r龙'>
<_sre.SRE_Match object; span=(0, 3), match='张\t龙'>
<_sre.SRE_Match object; span=(0, 3), match='张\x0c龙'>
None
None
ret = re.match("张\S龙", "张 龙")
print(ret)
ret = re.match("张\S龙", "张\n龙")
print(ret)
ret = re.match("张\S龙", "张\r龙")
print(ret)
ret = re.match("张\S龙", "张\t龙")
print(ret)
ret = re.match("张\S龙", "张\f龙")
print(ret)
ret = re.match("张\S龙", "张海龙")
print(ret)
ret = re.match("张\S龙", "张龙")
print(ret)
out:
None
None
None
None
None
<_sre.SRE_Match object; span=(0, 3), match='张海龙'>
None
import re
ret = re.match("张[\d\w\s#]龙", "张#龙")
print(ret)
ret = re.match("张[a-zA-Z0-9]龙", "张1龙")
print(ret)
# ^表示非
ret = re.match("张[^a-zA-Z0-9]龙", "张1龙")
print(ret)
ret = re.match("张[^a-zA-Z0-9]龙", "张海龙")
print(ret)
ret = re.match("张[^a-zA-Z0-9]龙", "张A龙")
print(ret)
out:
<_sre.SRE_Match object; span=(0, 3), match='张#龙'>
<_sre.SRE_Match object; span=(0, 3), match='张1龙'>
None
<_sre.SRE_Match object; span=(0, 3), match='张海龙'>
None
1.2.2 多字符匹配
| 方法 | 含义 |
|---|---|
| * | 匹配 0 或任意个前面的字符 |
| + | 匹配 1 或任意个前面的字符 |
| ? | 匹配 0 或 1 个前面的字符 |
| {m} | 匹配指定 m 个前面的字符 数量不足返回失败 |
| {n,m} | 匹配范围[n-m]数量前面的字符 数量不足返回失败 |
import re
s = "我的电话是12345678905."
# 匹配成功,但是是空,因为*是匹配0或任意个,匹配到了0个
ret = re.search("\d*", s)
print(ret)
# 匹配成功,因为+是匹配1或任意个
ret = re.search("\d+", s)
print(ret)
ret = re.search("\d?", s)
print(ret)
s = "0451-88888888"
ret = re.search("0\d+-\d{8}", s)
print(ret)
out:
<_sre.SRE_Match object; span=(0, 0), match=''>
<_sre.SRE_Match object; span=(5, 16), match='12345678905'>
<_sre.SRE_Match object; span=(0, 0), match=''>
<_sre.SRE_Match object; span=(0, 13), match='0451-88888888'>
1.2.3 边界匹配
r 开头引号的字符串表示正则表达式,不然有些会有转义效果
| 方法 | 含义 |
|---|---|
| ^ | 必须以规则开头匹配(电话) |
| $ | 必须以规则结尾匹配(邮箱) |
| \b | 位置两侧不可以都是单词字符 |
| \B | 位置两侧必须都是单词字符 |
| \A | 匹配字符串开始的位置 |
| \Z | 匹配字符串结束的位置 |
s = "asd"
ret = re.search(r"\ba\w+", s)
print(ret)
s = "sasd"
ret = re.search(r"\ba\w+", s)
print(ret)
out:
<_sre.SRE_Match object; span=(0, 3), match='asd'>
None
1.2.4 分组匹配
|符号匹配两边任意一边的正则表达式即可,
pattern参数写一个串
分组:
分组内部进行或匹配,外部相同。
group(0)是所有
例子:
import re
s = "我喜欢吃火锅,我喜欢吃小面,我喜欢吃拉面,我喜欢吃乌冬面,我喜欢吃烧鸡公,"
ret = re.search("我喜欢吃(火锅|小面)", s)
print(ret)
print(ret.group())
print(ret.group(0))
print(ret.group(1))
ret = re.search("(我喜欢吃)(火锅|小面)", s)
print(ret)
print(ret.group())
print(ret.group(0))
print(ret.group(1))
print(ret.group(2))
out:
<_sre.SRE_Match object; span=(0, 6), match='我喜欢吃火锅'>
我喜欢吃火锅
我喜欢吃火锅
火锅
<_sre.SRE_Match object; span=(0, 6), match='我喜欢吃火锅'>
我喜欢吃火锅
我喜欢吃火锅
我喜欢吃
火锅
复用分组匹配的内容:
s = "<h1>我是标题标签</h2>"
ret = re.match(r"<\w+>.*</\w+>", s)
print(ret)
ret = re.match(r"<(\w+)>.*</\1>", s)
print(ret)
s = "<h1>我是标题标签</h1>"
ret = re.match(r"<(\w+)>.*</\1>", s)
print(ret)
s = "<body><h1>我是标题标签</h1></body>"
ret = re.match(r"<(\w+)><(\w+)>.*</\2></\1>", s)
print(ret)
out:
<_sre.SRE_Match object; span=(0, 15), match='<h1>我是标题标签</h2>'>
None
<_sre.SRE_Match object; span=(0, 15), match='<h1>我是标题标签</h1>'>
<_sre.SRE_Match object; span=(0, 28), match='<body><h1>我是标题标签</h1></body>'>
分组命名:
定义:(?P<name>正则),P 是大写
使用:(?P=name)
例子:
s = "<body><h1>我是标题标签</h1></body>"
ret = re.match(r"<(?P<body>\w+)><(?P<h1>\w+)>.*</(?P=h1)></(?P=body)>", s)
print(ret)
out:
<_sre.SRE_Match object; span=(0, 28), match='<body><h1>我是标题标签</h1></body>'>
1.3 一些其他参数(Flag)
| 参数 | 含义 |
|---|---|
| re.IGNORECASE(re.I) | 忽略大小写 |
| re.MULTILINE(re.M) | 可换行(\n自动进入下一行) |
| re.ASCII(re.A) | 使\w/\W/d/\D/\s/\S/\b/\B 只匹配 ASCII 字符,不匹配 Unicode 字符 |
| re.Unicode(re.U) | 默认使用此标志位,\w/\W/d/\D/\s/\S/\b/\B 会匹配 Unicode 字符,如果指定了 re.A 标志,则 re.U 失效 |
| re.DOTALL(re.S) | 使得.会匹配换行符等其他字符 |
| re.VERBOSE(re.X) | 允许整个正则表达式写成多行,忽略空白字符,并可以添加#开头的注释,这样更美观。 |
1.4 贪婪和非贪婪
正则表达式默认情况下会尽可能多的匹配,
如:
asdf.png///123.png
如果写\w+.png不会匹配到 asdf.png
而是会匹配整个串
如果需要修改为非贪婪,就要在一些情况后面加上?操作符
包括:+,?,*,[n-m]
在使用的时候在这些符号后面加上?即可更改匹配模式为非贪婪
2.可迭代器
2.1 可迭代对象
使用iter()来获取可迭代对象(是可迭代对象就返回对象,如果不是就报错)
可迭代对象就是一个可以通过 iter 方法获取到迭代器(iterator)的对象。
可迭代对象:
- 对象实现了能返回迭代器的
__iter__()方法 - 对象实现了
__getitem__(index)方法,而且 index 参数是从 0 开始的整数(索引)
Python
中内置的序列类型,如list、tuple、str、bytes、dict、set、collections.deque等都可以迭代,因为都实现了__getitem__(index)方法。
注意:其实标准的序列还都实现了
__iter__()方法
2.1.1 判断是否是一个可迭代对象
from collections import abc
l = [1, 2, 3, 4]
ret = isinstance(l,abc.Iterable)
print(ret)
out:
True
最准确的方法还是调用iter(l),iter会考虑历史遗留的__getitem__(index)方法,abc.Iterable 只会考虑__iter()__方法。
2.1.2 构建可迭代对象
__iter__()__getitem__(index)
只要实现其中一个就是可迭代对象
如果实现了__iter__() 方法,但是该方法没有返回迭代器的时候
调用abc.Iterable会返回True的错误判断。
iter()会抛出异常
2.1.3iter()函数
Python 迭代器要求iter()必须返回特殊的迭代器对象。
迭代器对象必须实现__next__()方法,并使用StopIteration异常来通知迭代结束
iter()函数有两种使用方法:
iter(iterable) -> iterator:传入可迭代对象,返回迭代器iter(callable,sentinel) -> iterator:传入两个参数,第一个必须是可调用的对象(没有参数),用于不断调用,产出各个值,第二个值是==哨符==,是一个标记值,当第一个参数的调用返回了这个值的时候,触发迭代器抛出StopIteration异常,==不产出哨符==
例子:投骰子
import random
def d6():
return random.randint(1,6)
d6_iter = iter(d6 , 1)
print(d6_iter)
for it in d6_iter:
print(it)
out:
<callable_iterator object at 0x000002AC2B708A58>
4
6
例子:逐行读取文件,直到遇到空行或者达到文件末尾为止
with open('mydata.txt') as fp:
for line in iter(fp.readline, '\n'):
# fp.readline每次返回一行
print(line)
2.2 迭代器
内存中放不下数据集的时候,要使用一种惰性的获取数据的方式,即按需要一次获取一个数据对象。
这就是迭代器模式(Iterator pattern)
迭代器就是实现了无参数__next__()方法(无参数),返回序列中的下一个元素。
如果没有元素了,就抛出StopIteration异常。
即迭代器可以被next()函数调用,并不断返回下一个元素的值。
在 Python 语言内部, 迭代器 用于支持:
- for 循环
- 构建和扩展集合类型
- 逐行遍历文本文件
- 列表推导、字典推导和集合推导
- 元组拆包
- 调用函数时,使用*拆包实参
2.2.1 判断对象是否为迭代器
最好是使用isinstance(x,abc.Iterator)
from collections import abc
l = [1,2,3,4]
it = iter(l)
print(isinstance(l, abc.Iterable))
print(isinstance(l, abc.Iterator))
print(iter(l))
print(isinstance(it, abc.Iterable))
print(isinstance(it, abc.Iterator))
print(iter(it))
out:
True
False
<list_iterator object at 0x000001E5516AA2E8>
True
True
<list_iterator object at 0x000001E5515FB278>
Python 中内置的序列类型,如 list、tuple、str、bytes、dict、set、collections.deque 等都是 可迭代的对象,但不是迭代器; 生成器一定是迭代器
2.2.2__next__()和__iter__()
class A:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
# 返回一个迭代器
return self
def __next__(self):
# 返回下一个元素
# 有一个索引在记录每次返回的位置
try:
ret = self.data[self.index]
self.index += 1
except IndexError:
raise StopIteration()
return ret
iter 方法会调用__iter__方法返回当前迭代器对象
next 方法会调用__next__方法返回结果,同时索引增加(index 必须开始是 0 的整数)
2.2.3next()函数获取迭代器中下一个元素
除了可以用 for 循环处理迭代器中的元素外,可以直接使用next()方法
如果是最后一个,则 for 不会报错,
next()方法报错,原因是 for 循环是一个上下文管理器,会自动捕获异常,而 next()不会。其他迭代上下文也是如此。(列表推导、元组拆包等)
或者为next()函数指定第二个参数(默认值),执行到末尾后,返回默认值,不会抛出异常
with open('/etc/passwd') as fd:
while True:
line = next(fd, None)
if line is None:
break
print(line, end='')
2.2.4 可迭代的对象与迭代器的对比
Python 从可迭代对象中获取迭代器。
for 会先调用iter()方法将字符串转换成迭代器。然后进行迭代。
如果不使用 for 循环,则使用 while 来模拟:
it = iter("123")
while True:
try:
print(next(it))
except StopIteration:
del it
break
总结:
-
迭代器要实现
__next__()方法,返回迭代器中的下一个元素 -
迭代器还要实现
__iter__()方法,返回迭代器自身。==迭代器可以迭代,迭代器都是可迭代对象== -
可迭代对象一定要实现
__iter__()方法,返回一个迭代器,==但是不能返回自身!==也不能实现
__next__()方法
练习题:
1.截取
import re
s = """<div class="WB_text W_f14" node-type="feed_list_content" nick-name="林俊杰">飛機上偶遇的 J(JJ)F(Fish)J (Jimmy+Jolin)組合!<br><a
target="_blank" render="ext" extra-data="type=atname"
href="//weibo.com/n/%E6%A2%81%E9%9D%99%E8%8C%B9?from=feed&loc=at" usercard="name=梁静茹">@梁静茹</a> <a
target="_blank" render="ext" extra-data="type=atname"
href="//weibo.com/n/%E8%94%A1%E4%BE%9D%E6%9E%97?from=feed&loc=at" usercard="name=蔡依林">@蔡依林</a> <a
target="_blank" render="ext" extra-data="type=atname"
href="//weibo.com/n/%E5%A4%A2%E6%83%B3%E5%AE%B6%E6%9E%97%E5%BF%97%E7%A9%8E?from=feed&loc=at"
usercard="name=夢想家林志穎">@夢想家林志穎</a><br> 祝大家2018新年快樂!
</div>
"""
ret = re.sub("<br>.*<br>", " ", s, flags=re.S)
ret = re.findall(r"(?<=>)(.*?)<", ret, flags=re.S)
print(ret)
out:
['飛機上偶遇的 J(JJ)F(Fish)J (Jimmy+Jolin)組合! 祝大家2018新年快樂!\n']
2.邮箱验证
5-20 位单词字符
@
域名
.com
import re
s = "185699189@qq.com"
ret = re.match(r"^(\w{5,20})@(\w+.com)$", s)
print(ret.group(1))
print(ret.group(2))