正则表达式
本博客主要讲正则表达式在爬虫网页解析中的作用
需要的是python的re模块
python版本:3.x
(一) 正则表达式的基本知识
1 匹配字符
- 常见匹配模式—匹配字符
模式 | 描述 |
---|---|
. | 匹配任意除\n(换行符)之外的字符 |
\ | 转义字符 |
[…] | 用来表示一组字符,匹配[…]内的任意字符 |
^[…] | 匹配除了[…]内任意字符 |
- 预定义字符
模式 | 描述 |
---|---|
\d | 匹配数字[0-9] |
\D | 匹配非数字 |
\s | 匹配空白符 |
\S | 匹配非空白符 |
\w | 匹配26个字母 [a-z] [A-Z] |
\W | 匹配非26字母 |
a | b | 匹配a或b |
- 数量词
模式 | 描述 |
---|---|
* | 匹配*号前的字符 [0–多次] |
+ | 匹配+号前的字符 [1–多次] |
? | 匹配?前的字符 [0-1次] |
{m} | 匹配{}号前的字符m次 |
{m,n} | 匹配{}前的字符 [m-n次] |
- 边界匹配
模式 | 描述 |
---|---|
^ | 匹配字符串开头:^abc匹配abc开头字符串 |
$ | 匹配字符串结尾:xyz$匹配以xyz结尾的字符串 |
\A | 匹配字符串开始 |
\z | 匹配字符串结束 |
\Z | 匹配字符串结束,如果存在换行,只匹配到换行前的结束字符串 |
\G | 匹配最后匹配完成的位置 |
(…) | 表示一个表达式,也表示一个组,常见的是:(.*?) |
- 案例如下:
import re
content = 'Hello 123 4567 World_This is a Regex Demo'
# \s:匹配空白符
# \d:匹配数字
# \d{4}:匹配4个数字
# \w{10}:匹配10个单个字符
#.:匹配任意字符
#*:匹配0-多个表达式
result =re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}.*',content)
# 改进 result =re.match('^He.*?(\d+)\s\d{4}\s\w{10}.*',content)
#因为没有组,所以为0
print(result.group(0))
#范围 输出结果是从0匹配到41
print(result.span())
'''
输出结果:
(0, 41)
Hello 123 4567 World_This is a Regex Demo
'''
2 匹配函数
- 函数和描述
函数 | 描述 |
---|---|
re.match(pattern,content) | 从content字符串的起点开始匹配 |
re.search(pattern,content) | 对content做任意位置匹配 |
re.findall(pattern,content) | 从满足位置开始找出所有满足正则表达式的字符串,返回的是列表 |
re.finditer(pattern,content) | 从满足位置开始找出所有满足正则表达式的字符串,返回的是迭代器 |
- 代码对比
import re
content = '''wo men Hello 1234567 World_This is
a Regex Demo
'''
result = re.match('He.*?(\d+).*?Demo',content,re.S)
result1 = re.search('He.*?(\d+).*?Demo',content,re.S)
print(result)
print(result1)
'''
输出:
None
<_sre.SRE_Match object; span=(7, 47), match='Hello 1234567 World_This is\na Regex Demo'>
'''
注意:当遇到有换行的时候要用 : re.S
3 贪婪和非贪婪匹配
- 贪婪匹配:.*
#贪婪匹配
import re
content = 'Hello 1234567 World_This is a Regex Demo'
#.*:尽可能匹配多,而后面有个(\d+)中的+至少匹配一个,所以(\d+)匹配了7
result = re.match('^He.*(\d+).*Demo$',content)
#例如(\d*),由于*是匹配0-无限个,导致(\d*)没匹配到
result1 = re.match('^He.*(\d*).*Demo$',content)
print(result.group(1))
'''
输出:7
注意result1没有匹配到
'''
- 非贪婪匹配:.*?
#非贪婪匹配: .*? : 匹配尽可能少的
import re
content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^He.*?(\d+).*Demo$',content)
print(result.group(1))
'''
输出:1234567
'''
类似优先级:当时贪婪时优先级大于后面的匹配符
当时非贪婪时,优先级小于后面的匹配符
(二)豆瓣实战
- 网页部分主要代码
对HTML代码分析
<li class="">
<div class="cover">
<a href="https://book.douban.com/subject/30370935/?icn=index-editionrecommend" title="海胆">
<img src="https://img3.doubanio.com/view/subject/m/public/s29918393.jpg" class="" width="115px" height="172px" alt="海胆">
</a>
</div>
<div class="intervenor-info">
<img src="https://img3.doubanio.com/f/book/ef040178fab1770d60e3f2f12ba4c7aa70714396/pics/book/partner/jd_recommend.png" class="jd-icon" width="16" height="16">
<span>推荐</span>
</div>
<div class="info">
<div class="title">
<a class="" href="https://book.douban.com/subject/30370935/?icn=index-editionrecommend" title="海胆">海胆</a>
</div>
<div class="author">
雷晓宇
</div>
<div class="more-meta">
<h4 class="title">
海胆
</h4>
<p>
<span class="author">
雷晓宇
</span>
/
<span class="year">
2018-11
</span>
/
<span class="publisher">
浙江文艺出版社
</span>
</p>
<p class="abstract">
《海胆》是一本人物特写集,收录了十篇文章,写出了十个人的秘密。
附赠朴树亲笔信。
之所以取名“海胆”,是因为这十个人都和海胆一样:有尖利的刺,也有柔软的心。
除了刷爆朋友圈的《和李安一起午餐》——对李安而言,电影的秘密可以讲,生活的秘密不可说。但他把生活秘密藏在电影里,看懂了,才是真正理解他了——还有:
《Hello,朴树先生》:用两万字告诉你,一个4...
</p>
</div>
</div>
</li>
写正则表达式:#从标签<li>开始,要注意标签闭合以防匹配失败
'<li.*?cover.*?href="(.*?)".?title="(.*?)".*?author">(.*?)</div>.*?year">(.*?)</span>'
- 完整代码如下:
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)
result = re.findall(pattern,content)
#print(result)
for res in result:
url,name,author,date = result
author = re.sub('\s',''author)
date = re.sub('\s',''date)
print(url,name,author,date)