崔庆才老师爬虫的学习笔记。

一、Re库详解

1、什么是正则表达式

正则表达式对子符串操作的一种逻辑公式,就是事先定义好的一些特定字符、及这些特定字符的组合,组成一个‘规则字符串’,这个‘规则字符串’用来表达对字符串的一种过滤逻辑。

2、样例展示

开源中国提供的正则表达式测试工具:http://tool.oschina.net/regex/ 。输入待匹配的文本,然后选择常用的正则表达式,就可以得出相应的匹配结果了。

3、常用的匹配规则

模式 描述
\w 匹配字母、数字及下划线
\W 匹配不是字母、数字及下划线的字符
\s 匹配任意空白字符,等价于[\t\n\r\f]
\S 匹配任意非空字符
\d 匹配任意数字,等价于[0-9]
\D 匹配任意非数字的字符
\A 匹配字符串开头
\Z 匹配字符串结尾,如果存在换行,只匹配到换行前的结束字符串
\z 匹配字符串结尾,如果存在换行,同时还会匹配换行符
\G 匹配最后匹配完成的位置
\n 匹配一个换行符
\t 匹配一个制表符
^ 匹配一行字符串的开头
$ 匹配一行字符串的结尾
. 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符
[…] 用来表示一组字符,单独列出,比如[amk]匹配a、m或k
[^…] 不在[]中的字符,比如[^abc]匹配除了a、b、c之外的字符
* 匹配0个或多个表达式
+ 匹配1个或多个表达式
? 匹配0个或1个前面的正则表达式定义的片段,非贪婪方式
{n} 精确匹配n个前面的表达式
{n, m} 匹配n到m次由前面正则表达式定义的片段,贪婪方式
a|b 匹配a或b
( ) 匹配括号内的表达式,也表示一个组

4、常用的修饰符(匹配模式)

修饰符 描述
re.I 使匹配对大小写不敏感
re.L 做本地化识别(locale-aware)匹配
re.M 多行匹配,影响^和$
re.S 使.匹配包括换行在内的所有字符
re.U 根据Unicode字符集解析字符。这个标志影响\w、\W、 \b和\B
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解

5、re.match

尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回None

re.match(pattern,string,flags=0)

1
2
3
4
5
6
7
8
9
#常规匹配
import re

content = 'Hello 123 4567 World_This is a Regex Demo'
print(len(content))
result = re.match('^Hello\s\d\d\d\s\d{4}.*Demo$',content)
print(result)
print(result.group())
print(result.span())
1
2
3
4
5
6
7
8
9
#泛匹配
import re

content = 'Hello 123 4567 World_This is a Regex Demo'
print(len(content))
result = re.match('^Hello.*Demo$',content)
print(result)
print(result.group())
print(result.span())
1
2
3
4
5
6
7
8
9
10
11
12
#匹配目标
import re

content = 'Hello 1234567 World_This is a Regex Demo'
print(len(content))
#可以使用()括号将想提取的子字符串括起来。()实际上标记了一个子表达式的开始和结束位置,被标记的每个子表达式会依次对应每一个分组,调用group()方法传入分组的索引即可获取提取的结果。
result = re.match('^Hello\s(\d+)\sWorld.*Demo$',content)
print(result)
print(result.group())
print(result.group(1)) # 将第一括号括起来的内容打印出来,依次可推group(2)
print(result.span())
print(result.groups())
1
2
3
4
5
6
7
8
9
#贪婪匹配
import re

content = 'Hello 1234567 World_This is a Regex Demo'
print(len(content))
result = re.match('^Hel.*(\d+).*Demo$',content)
print(result)
print(result.group(1))
#在贪婪匹配下,.*会匹配尽可能多的字符。正则表达式中.*后面\d+至少一个数字,但并没有指定具体多少个数字,因此,.*就尽可能匹配多的字符,这里就把123456匹配了,给\d+留下一个可满足条件的数字7。
1
2
3
4
5
6
7
8
#非贪婪匹配
content = 'Hello 1234567 World_This is a Regex Demo'
print(len(content))
result = re.match('Hel.*?(\d+).*Demo$',content) #非贪婪匹配的写法是.*?
print(result)
print(result.group(1))
#非贪婪匹配就是尽可能匹配少的字符。当.*?匹配到Hello后面的空白字符时,再往后的字符就是数字了,而\d+恰好可以匹配,那么这里.*?就不再进行匹配,交给\d+去匹配后面的数字。
#这里需要注意,如果匹配的结果在字符串结尾,.*?就有可能匹配不到任何内容了,因为它会匹配尽可能少的字符。需要用贪婪匹配.*
1
2
3
4
5
6
7
8
9
#匹配模式(修饰符)
import re

content = '''Hello 1234567 World_This
is a Regex Demo'''
result = re.match('He.*?(\d+).*?Demo$',content) #.匹配的是除换行符之外的任意字符,当遇到换行符时,.*?就不能匹配了
print(result)
result1 = re.match('He.*?(\d+).*?Demo$',content,re.S) # 只需加一个修饰符re.S,使.匹配包括换行在内的所有字符
print(result1)
1
2
3
4
5
6
7
8
#转义匹配
import re

content = 'prince is $5.00'
result = re.match('prince is $5.00',content)
print(result)
result1 = re.match('prince is \$5\.00',content) #遇到用于正则匹配模式的特殊字符时,在前面加反斜线转义一下
print(result1)

总结:尽量使用泛匹配,使用括号得到匹配目标,尽量使用非贪婪模式、有换行符就用re.S

match()方法是从字符串的开头开始匹配的,一旦开头不匹配,那么整个匹配就失败了.

这里有另外一个方法search()会依次扫描字符串,直到找到第一个符合规则的字符串,然后返回匹配内容,如果搜索完了还没有找到,就返回None。

1
2
3
4
5
6
7
import re

content = 'Extra stings Hello 1234567 World_This is a Regex Demo Extra stings'
result = re.match('Hello.*?(\d+).*?Demo', content)
print(result)
result1 = re.search('Hello.*?(\d+).*?Demo', content)
print(result1)

总结:为了匹配方便,能用search()就不用match()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 匹配练习
import re

html = '''<div id="songs-list">
<h2 class="title">经典老歌</h2>
<p class="introduction">
经典老歌列表
</p>
<ul id="list" class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4" class="active">
<a href="/3.mp3" singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3" singer="邓丽君"><i class="fa fa-user"></i>但愿人长久</a>
</li>
</ul>
</div>'''

result = re.search('<li.*?active.*?singer="(.*?)">(.*?)</a>',html,re.S)
print(result.groups())
print(result.group(1),result.group(2))

7、re.findall

搜索字符,以列表的形式返回全部匹配的字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import re

html = '''<div id="songs-list">
<h2 class="title">经典老歌</h2>
<p class="introduction">
经典老歌列表
</p>
<ul id="list" class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4" class="active">
<a href="/3.mp3" singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3" singer="邓丽君">但愿人长久</a>
</li>
</ul>
</div>'''

results = re.findall('<li.*?href="/(.*?)".*?singer="(.*?)">(.*?)</a>',html,re.S)
print(results)
print(type(results))
for result in results:
print(result[0],result[1],result[2])

results1 = re.findall('<li.*?>\s*?(</a.*?>)?(\w+)(</a>)?\s*?</li>',html,re.S)
print(results1)
for result1 in results1:
print(result1[1])

8、re.sub

替换字符串中每一个匹配的字符串后返回替换后的字符串。

re.sub(正则表达式,要替换成的字符串,原字符串)

1
2
3
4
5
6
7
8
9
10
11
12
import re 

content = 'Extra stings Hello 1234567 World_This is a Regex Demo Extra stings'

result = re.sub('\d+','',content) #去掉数字
print(result)

result1 = re.sub('\d+','Replacement',content) #数字替换为字符
print(result1)

result2 = re.sub('(\d+)',r'\1 8910',content) #如果要替换的字符串包含自己本身用\1表示,正则表达式需要用()
print(result2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import re 

html = '''''<div id="songs-list">
<h2 class="title">经典老歌</h2>
<p class="introduction">
经典老歌列表
</p>
<ul id="list"class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href="/2.mp3"singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4"class="active">
<a href="/3.mp3"singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3"singer="begoud">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3"singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3"singer="邓丽君">但愿人长久</a>
</li>
</ul>
</div>'''

html = re.sub('<a.*?>|</a>','',html) #首先去掉a标签
print(html)
results = re.findall('<li.*?>(.*?)</li>',html,re.S) #然后再查找所有歌名
print(results)
for result in results:
print(result.strip())

9、re.compile

将正则字符串编译成正则表达式对象,以便在后面的匹配中复用。

1
2
3
4
5
6
7
8
9
10
import re 

content = '''Hello 1234545 World_This
is a Regex Demo'''

pattern = re.compile('Hello.*Demo',re.S) #编译正则表达式对象
result = re.match(pattern,content) #正则对象复用
print(result)
result1 = re.match('Hello.*Demo',content,re.S)
print(result1)

10、实战练习

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
import re

content = requests.get('https://book.douban.com/').text #获取网页源代码
#print(content)
pattern = re.compile('<li.*?"cover">.*?href="(.*?)" title="(.*?)".*?"more-meta".*?"author">(.*?)</span>.*?"year">(.*?)</span>.*?</li>',re.S)
results = re.findall(pattern,content)
print(results)
for result in results:
url,name,author,time = result
author = re.sub('\s','',author)
time = re.sub('\s','',time)
print(url,name,author,time)

持续更新…

最后更新: 2018年08月14日 17:29

原始链接: http://pythonfood.github.io/2018/07/02/爬虫-re库/

× 多少都行~
打赏二维码