正则表达式,又称规则表达式,是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
正则、Xpath、BeautifulSoup对比
正则表达式的语法
正则的语法很多,这里列出常用的
re模块的常用方法
-
match()方法用于从字符串的第一个字符开始匹配,如果匹配成功,则返回Match对象,否则返回None
-
search()方法用于在整个字符串中搜索第一个匹配的值,如果匹配成功,则返回Match对象,否则返回None
-
findall()方法用于在整个字符串中搜索所有符合正则表达式的字符串,如果匹配成功,返回包含匹配结构的列表,否则返回空列表
-
sub()方法用于替换字符串
-
split()方法用于根据正则表达式分割字符串,并以列表的形式
范例代码一:
import re
string = '小明:20岁,小红:19岁'
pattern = '\d{2}'
result = re.match(pattern,string)
print(result)
result = re.search(pattern,string)
print(result)
print(result.group())
result = re.findall(pattern,string)
print(result)
运行结果
None
<re.Match object; span=(3, 5), match='20'>
20
['20', '19']
>>>
match()方法得到的结果是None,原因在于match()方法是从字符串第一个字符开始去匹配的,这里因string第一个字符不是数字,所以直接返回None,如果直接print(result.group())
是会报错的。
group()
方法是Match对象
获取匹配的内容
范例代码二:
import re
content = '发布于2019/7/28,发布人:小明'
result =re.match('.*?(\d.*\d).*:(.*)',content)
print(result.group())
print(result.group(0))
print(result.group(1))
print(result.group(2))
print('')
result =re.findall('.*?(\d.*\d).*:(.*)',content)
print(result)
print(result[0])
print(result[0][0])
print(result[0][1])
运行结果:
发布于2019/7/28,发布人:小明
发布于2019/7/28,发布人:小明
2019/7/28
小明
[('2019/7/28', '小明')]
('2019/7/28', '小明')
2019/7/28
小明
>>>
可以发现,group是针对()来说的,group(0)
就是指的整个字符串,group(1)
指的是第一个括号里的内容,group(2)
指的第二个括号里的内容。
findall()方法匹配多个字符时,返回列表的元素为元组
.*?
非贪婪模式,它匹配到'于'
之后就停止了
把上面的代码修改一下,我们希望提取发布时间,即2019/7/28
范例代码三:
import re
content = '发布于2019/7/28,发布人:小明'
result =re.findall('.*?(\d.*\d)',content)
print(result)
result =re.findall('.*(\d.*\d)',content)
print(result)
result =re.findall('.*(\d.*?\d)',content)
print(result)
result =re.findall('.*?(\d.*?\d)',content)
print(result)
运行结果:
['2019/7/28']
['28']
['28']
['20', '19', '7/2']
>>>
.*?
是非贪婪模式,尽可能少量地去匹配
.*
是贪婪模式,尽可能大量地去匹配
我们先看括号内的内容,前后都是\d
,说明()
内的匹配结果必须包含两个数字,并且一前一后,范围在2019/7/28
之内
对于规则字符串.*?(\d.*\d)
,.*?
只要满足它的后面有两个数字它就不管了,所以它取到于
字就停止了,因此.*?
匹配为发布于
,这样第一个\d
匹配为2019
的2
,.*
是贪婪匹配,它只要满足它后面有一个数字就继续取值,所以.*
匹配为019/7/2
,所以第二个\d
就是8
了
对于规则字符串.*(\d.*\d)
,第一个.*
实行贪婪匹配,它发现它直接取到发布于2019/7/
,剩下28
留给\d.*\d
去匹配依然满足,所以它就这么干了
对于规则字符串.*(\d.*?\d)
,和上面一样
以上三条,由于整个规则字符串只能成功匹配一次,因此返回一个元素的列表
对于规则字符串.*?(\d.*?\d)
,第一个.*?
取到于
字就不管了,剩下\d.*?\d
去匹配2019/7/28
,而.*?
是非贪婪匹配,也叫懒惰匹配,当第一个\d
匹配为2019
的2
时,它发现它不去匹配字符也行(其实是匹配为空值,即*
=0),所以第二个\d
匹配为0
,就是\d.*?\d
第一次匹配为20
,剩下19/7/28
还可以继续匹配;因为findall()方法是要获取所有匹配成功的结果,所以还得继续匹配,第二次匹配为19
,剩下/7/28
,只能从7
开始匹配,第三次匹配为7/2
,剩下8
,无法继续匹配了
范例代码四:
import re
string = '香辣口水鸡 卤肉饭 蒜蓉烤生蚝'
result = re.sub('\s+',' ',string)
print(result)
运行结果:
香辣口水鸡 卤肉饭 蒜蓉烤生蚝
>>>
范例代码五:
import re
url = 'https://zhidao.baidu.com/list?cid=110&fr=daohang'
pattern = r'[?|&]'
result = re.split(pattern,url)
print(result)
运行结果:
['https://zhidao.baidu.com/list', 'cid=110', 'fr=daohang']
>>>
范例代码六:
import re
pattern = r'china_\w+'
string = 'CHINA_666 china_666'
reult = re.match(pattern,string,re.I)
print(reult.group())
运行结果:
CHINA_666
>>>
上面,匹配字符串是否以"china_"开头,不区分大小写
re.I
是标志参数,表示执行不区分字母大小写的匹配
常用的标志如下:
re.I
表示执行不区分字母大小写的匹配
re.S
匹配所有字符,包括换行符
re.X
忽略规则字符串中未转义的空格和注释
re通用匹配公式
用这个通用匹配公式实现对html页面快速提取数据
reult = re.findall(r'~~
(.*?)~~
',string,re.S)
波浪线部分代表有标识型一串字符
实例展示:
目标网站:豆瓣图书 Top 250 https://book.douban.com/top250
提取数据:书名、评分、推荐语、链接
-
分析url:
通过多次翻页发现:第i
页对应的url = 'https://book.douban.com/top250?start='+str(i*25)
-
分析网页:
每本书的信息在<tr class="item">标签下,用re通用匹配公式得出单个html页面的所有书books = re.findall(r'<tr class="item">(.*?)</tr>',html,re.S)
再用for语句遍历,for book in books
,book
就是每本书所有信息,对book
用re公式提取标签的内容,将返回包含一个元素的列表
为了防止某个书的信息不全,可以写个if
判断语句
代码实现:
import requests
import re
# 如果匹配不成功,返回空字符串,成功则取值
def info(list_name):
if list_name==[]:
return ''
else:
return list_name[0]
# 用正则提取数据
def get_data(url,headers):
html = requests.get(url,headers = headers).text
books = re.findall(r'<tr class="item">(.*?)</tr>',html,re.S)
for book in books:
title = re.findall(r'title="(.*?)"',book,re.S)
num = re.findall(r'<span class="rating_nums">(.*?)</span>',book,re.S)
introduce= re.findall(r'<span class="inq">(.*?)</span>',book,re.S)
link = re.findall(r'class="nbg" href="(.*?)"',book,re.S)
print(info(title),info(num),info(introduce),info(link))
if __name__ == "__main__":
for i in range(10):
url = 'https://book.douban.com/top250?start='+str(i*25)
headers = {'User-Agent': 'Mozilla/5.0'}
get_data(url,headers)
>>>
阅读更多文章请点击以下链接:
python爬虫从入门到放弃之一:认识爬虫
python爬虫从入门到放弃之二:HTML基础
python爬虫从入门到放弃之三:爬虫的基本流程
python爬虫从入门到放弃之四:Requests库基础
python爬虫从入门到放弃之五:Requests库高级用法
python爬虫从入门到放弃之六:BeautifulSoup库
python爬虫从入门到放弃之七:正则表达式
python爬虫从入门到放弃之八:Xpath
python爬虫从入门到放弃之九:Json解析
python爬虫从入门到放弃之十:selenium库
python爬虫从入门到放弃之十一:定时发送邮件
python爬虫从入门到放弃之十二:多协程
python爬虫从入门到放弃之十三:Scrapy概念和流程
python爬虫从入门到放弃之十四:Scrapy入门使用
网友评论