目录
摘要:为什么学正则表达式
实际上爬虫一共就四个主要步骤:
1.明确目标 (要知道你准备在哪个范围或者网站去搜索)
2.爬 (将所有的网站的内容全部爬下来)
3.取 (去掉对我们没用处的数据)
4.处理数据(按照我们想要的方式存储和使用)
而正则表达式就是应用于取这一步,他的主要作用就是筛选所需要的数据
一、 什么是正则表达式
正则表达式,又称规则表达式,通常是被用来检索、替换那些符合某个模式(规则)的文本。
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
例如:
例题 1:我们想要找一篇英文文献里面的所有 we 的单词 文本:wearewellWelcome
正则表达式:we 匹配所有的含有 we 的
但是包括 well welcome 正则表达式:\bwe\b 只匹配了 we
\b 是正则表达式的特殊字符,称为元字符,匹配的是单词的开始和结束,也就是单词的 分界处。
二、 python中的re模块
在 Python 中,我们可以使用内置的 re 模块来使用正则表达式。
有一点需要特别注意的是,正则表达式使用 对特殊字符进行转义,所以如果我们要使用原始字符串,只需加一个 r 前缀。
示例:
print(r'chuanzhiboke\t\.\tpython')
三、 re 模块的使用步骤
1.使用 compile() 函数将正则表达式的字符串形式编译为一个 Pattern 对象
2.通过Pattern 对象提供的一系列方法对文本进行匹配查找,获得匹配结果,一个 Match 对象。
3.最后使用 Match 对象提供的属性和方法获得信息,根据需要进行其他的操作
compile 函数
compile 函数用于编译正则表达式,生成一个 Pattern 对象,它的一般使用形式如下:
import re
# 将正则表达式编译成 Pattern 对象
pattern = re.compile(r'\d+')
在上面,我们已将一个正则表达式编译成 Pattern 对象,接下来,我们就可以利用 pattern 的一系列方法对文本进行匹配查找了。
Pattern 对象
常用方法有:
1.match 方法:从起始位置开始查找,一次匹配
2.search 方法:从任何位置开始查找,一次匹配
3.findall 方法:全部匹配,返回列表
4.finditer 方法:全部匹配,返回迭代器
5.split 方法:分割字符串,返回列表
6.sub 方法:替换
1.match 方法
match 方法用于查找字符串的头部(也可以指定起始位置),它是一次匹配,只要找到了一个匹配的结果就返回,而不是查找所有匹配的结果。(不常用)
import re
pattern = re.compile(r'\d+') # 用于匹配至少一个数字
m = pattern.match('one12twothree34four') # 查找头部,没有匹配
print(m) # None
m = pattern.match('one12twothree34four', 2, 10) # 从'e'的位置开始匹配,没有匹配
print(m) # None
m = pattern.match('one12twothree34four', 3, 10) # 从'1'的位置开始匹配,正好匹配
print(m) # 返回一个 Match 对象<_sre.SRE_Match object at 0x10a42aac0>
print( m.group(0) ) # 可省略 0 结果:12 匹配到的字符串的内容
print( m.start(0) ) # 可省略 0 结果:3 匹配的字符串的起始位置
print( m.end(0) ) # 可省略 0 结果:5 匹配的到字符串的结束为止
print( m.span(0) ) # 可省略 0 结果:(3, 5) 匹配到字符串的范围
再看一个例子:
import re
pattern = re.compile(r'([a-z]+) ([a-z]+)', re.I) # re.I 表示忽略大小写
m = pattern.match('Hello World Wide Web')
print(m) # 匹配成功,返回一个 Match 对象<_sre.SRE_Match object at 0x10bea83e8>
print(m.group(0)) # 返回匹配成功的整个子串'Hello World'
print(m.span(0)) # 返回匹配成功的整个子串的索引(0, 11)
print(m.group(1)) # 返回第一个分组匹配成功的子串'Hello'
print(m.span(1)) # 返回第一个分组匹配成功的子串的索引(0, 5)
print(m.group(2)) # 返回第二个分组匹配成功的子串'World'
print(m.span(2)) # 返回第二个分组匹配成功的子串(6, 11)
print(m.groups()) # 等价于 (m.group(1), m.group(2), ...)('Hello', 'World')
print(m.group(3)) # 不存在第三个分组Traceback (most recent call last): File "<stdin>", line 1, in <module>IndexError: no such group
2.search 方法
search 方法用于查找字符串的任何位置,它也是一次匹配,只要找到了一个匹配的结果就返回,而不是查找所有匹配的结果
(常用)
例子:
import re
pattern = re.compile(r'\d+')
content = 'one12twothree34four'
m1 = pattern.search(content)
print(m1) # <_sre.SRE_Match object; span=(3, 5), match='12'>
print(m1.group()) # 12
m2 = pattern.search(content,10,30) # 指定字符串区间
print(m2) # <_sre.SRE_Match object; span=(13, 15), match='34'>
print(m2.group()) # 34
print(m2.span()) # (13, 15)
3.findall 方法
上面的 match 和 search 方法都是一次匹配,只要找到了一个匹配的结果就返回。然而,在大多数时候,我们需要搜索整个字符串,获得所有匹配的结果。
(常用)
import re
pattern = re.compile(r'\d+') # 查找数字
result1 = pattern.findall('hello 123456 789')
result2 = pattern.findall('one1two2three3four4', 0, 10)
print(result1) # ['123456', '789']
print(result2) # ['1', '2']
再来一个例子:
import re
pattern = re.compile(r'\d+\.\d*') # 通过partten.findall()方法就能够全部匹配到我们得到的字符串
result = pattern.findall("123.141593, 'bigcat', 232312, 3.15, 2.1")
# findall 以 列表形式 返回全部能匹配的子串给result
for item in result:
print(item)
"""
结果:
123.141593
3.15
2.1
"""
4.finditer 方法
finditer 方法的行为跟 findall 的行为类似,也是搜索整个字符串,获得所有匹配的结果。但它返回一个顺序访问每一个匹配结果(Match 对象)的迭代器。
import re
pattern = re.compile(r'\d+')
content1 = 'hello 123456 789'
content2 = 'one1two2three3four4'
result_iter1 = pattern.finditer(content1)
result_iter2 = pattern.finditer(content2,0,10)
print(type(result_iter1)) # 可迭代对象
print(type(result_iter2)) # 可迭代对象
print('result1......')
for m1 in result_iter1:
print('matching string:{},position:{}'.format(m1.group(),m1.span()))
print('result2......')
for m2 in result_iter2:
print('matching string:{},position:{}'.format(m2.group(),m2.span()))
"""
结果:
<class 'callable_iterator'>
<class 'callable_iterator'>
result1......
matching string:123456,position:(6, 12)
matching string:789,position:(13, 16)
result2......
matching string:1,position:(3, 4)
matching string:2,position:(7, 8)
"""
5.split 方法
split 方法按照能够匹配的子串将字符串分割后返回列表,它的使用形式如下:
split(string[, maxsplit])
其中,maxsplit 用于指定最大分割次数,不指定将全部分割。
import re
p = re.compile(r'[\s\,\;]+')
#,,;
s = ' ;;;;,,, ,,,,, '
print(p.search(s).group())
a = p.split('a,b;;c ;;;;,,, ,,,,, d')
print(a) #['a', 'b', 'c', 'd']
6.sub 方法
sub 方法用于替换。它的使用形式如下:
sub(repl, string[, count])
其中,repl 可以是字符串也可以是一个函数:
如果 repl 是字符串,则会使用 repl 去替换字符串每一个匹配的子串,并返回替换后的字符串,另外,repl 还可以使用 id 的形式来引用分组,但不能使用编号 0;
如果 repl 是函数,这个方法应当只接受一个参数(Match 对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。
count 用于指定最多替换次数,不指定时全部替换。
import re
p = re.compile(r'(\w+) (\w+)')
s = 'hello 123,hello 456'
# print(p.sub(r'hello world',s))#使用‘hello world'替换'hello 123'和'hello 456'
def func(m):
return 'hi'+' '+m.group(2)
print(p.sub(func,s),"*")
print(p.sub(func,s,1),"**") # 最多替换一次
四、贪婪模式与非贪婪模式
示例一 :
源字符串:
abbbc
使用贪婪的数量词的正则表达式 ab* ,匹配结果: abbb。
决定了尽可能多匹配 b,所以a后面所有的 b 都出现了。
使用非贪婪的数量词的正则表达式ab*?,匹配结果: a。
即使前面有 *,但是 ? 决定了尽可能少匹配 b,所以没有 b。
示例二 :
源字符串:
aa<div>test1</div>bb<div>test2</div>cc
1.使用贪婪的数量词的正则表达式:
<div>.*</div>
匹配结果:
<div>test1</div>bb<div>test2</div>
这里采用的是贪婪模式。在匹配到第一个“”时已经可以使整个表达式匹配成功,但是由于采用的是贪婪模式,所以仍然要向右尝试匹配,查看是否还有更长的可以成功匹配的子串。匹配到第二个“”后,向右再没有可以成功匹配的子串,匹配结束,匹配结果为“
2.使用非贪婪的数量词的正则表达式
<div>.*?</div>
匹配结果:
<div>test1</div>
正则表达式二采用的是非贪婪模式,在匹配到第一个“”时使整个表达式匹配成功,由于采用的是非贪婪模式,所以结束匹配,不再向右尝试