Author: Labyrinthine Leo
Init_time: 2021.02.22
Key Words: Spider
、re
公众号:Leo的博客城堡
**前言:**前几节学习了如何使用requests
模块对普通静态页面和异步加载数据页面信息进行爬取,这些都是对整个网页获取数据或者获取响应的json
数据,还未涉及到开头之前降到的聚焦爬虫
中的数据解析。从这一节开始进行网页数据解析的案例实战。
Beautiful Soup
库xpath
方法(最常用)其实聚焦爬虫中关注的局部文本内容,通常是在页面HTML
源码中的标签之间或者标签对应的属性中。
正则表达式,大家应该听得很熟了,那它到底是什么,为什么爬虫中经常使用,那就一看究竟。
正则表达式(regular expression
)描述了一种字符串匹配的模式(pattern
),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。通俗理解:就是使用一种规则性的表达式来寻找文本字符串中通用的数据。
这里博主为了方便大家快速入门,只会对基础的语法知识进行讲解,如要进阶还需查阅更多官网文档。
正则表达式包括普通字符和特殊字符,而这里的特殊字符其实就是元字符,因此本文主要对元字符进行简单讲解。
.
)点(.
)可以匹配所有字符:匹配出换行符以外的任意单个字符(按行匹配)
示例:
# coding : utf-8
# fun : 正则表达式demo
# @Author : Labyrinthine Leo
# @Time : 2021.02.01
import re
content = """苹果是绿色的
橙子是橙色的
香蕉是黄色的
乌鸦是黑色的
"""
p = re.compile(r'.色')
for one in p.findall(content):
print(one)
注意:re
模块是使用正则表达式的python官方库,在使用之前需要进行import
,其中re.compile()
表示对正则表达式进行编译,将普通的字符串编译为可通配的正则表达式。p.findall(content)
用于在文本中通过表达式进行匹配数据。结果如下:
*
)星号(*
)重复匹配任意次:表示匹配前面的子表达式任意次,包括0次
示例:
# coding : utf-8
# fun : 正则表达式demo
# @Author : Labyrinthine Leo
# @Time : 2021.02.01
import re
content = """苹果,是绿色的
橙子,是橙色的
香蕉,是黄色的
乌鸦,是黑色的
猴子,
"""
p = re.compile(r',.*')
for i in p.findall(content):
print(i)
解析:',.*'
表示匹配前面一个字符是全角逗号,后面紧接着多个字符,可以发现结果都是按行匹配的。结果如下:
+
)加号(+
)重复匹配多次:表示匹配前面的子表达式一次或多次,不包括0次(用法同上)
{}
)花括号({}
)匹配指定次数:表示前面的字符匹配指定的次数,其表示范围(最小次数和最大次数)
示例:
# coding : utf-8
# fun : 正则表达式demo
# @Author : Labyrinthine Leo
# @Time : 2021.02.01
import re
content = """
红彤彤,绿油油,黑乎乎,绿油油油
"""
p = re.compile(r"绿油{3,4}")
for i in p.findall(content):
print(i)
解析:'绿油{3,4}'
表示匹配绿字且紧接着的油字出现最少3次最多4次的字符串,符合的只有绿油油油和绿油油油油。结果如下:
?
)问号(?
)匹配0或1次:表示前面的字符匹配0或1次,等价于{0,1}
假设我们需要将下面字符串中所有的html
标签提取出来:
source = 'Title '
得到这样的列表:
['','','' ,'']
于是我们使用以下代码:
import re
p = re.compile(r'<.*>')
print(p.findall(source))
出现的匹配结果如下:
['Title ']
我们代码中r'<.*>'
表示匹配所以包含左右尖括号,中间含任意个字符的字符串,即我们想要的html
标签,但是匹配的结果与我们预想的并不一样,这是为何呢?因为我们前面讲述的*
、+
、?
都是贪婪性质的,所谓贪婪,是指其尽可能匹配更多的字符内容。而在这里我们想要的是让其一个标签一个标签的匹配,那就需要非贪婪模式,非贪婪模式其实就在匹配次数后加?
即可,这里的话我们使用r'<.*?>'
会发现即可将标签一个一个匹配出来。
示例:
# coding : utf-8
# fun : 正则表达式demo
# @Author : Labyrinthine Leo
# @Time : 2021.02.01
import re
source = 'Title '
p = re.compile(r'<.*?>')
print(p.findall(source))
结果如下:
\
)反斜杠(\
)对元字符转义:在正则表达式中有多种用途
# coding : utf-8 # fun : 正则表达式demo:反斜杠转义 # @Author : Labyrinthine Leo # @Time : 2021.02.01 import re content = """ 苹果. 是绿色的 橙子. 是橙色的 香蕉. 是黄色的 乌鸦. 是黑色的 """ p = re.compile(r'.*\.') for i in p.findall(content): print(i)
结果:
\d
匹配0-9之间任意一个数字字符,等价于表达式[0-9]
\D
匹配任意一个不是0-9之间的数字字符,等价于表达式[^0-9]
\s
匹配任意一个空白字符,包括 空格、tab、换行符等,等价于表达式[\t\n\r\f\v]
\S
匹配任意一个非空白字符,等价于表达式[^ \t\n\r\f\v]
\w
匹配任意一个文字字符,包括大小写字母、数字、下划线,等价于表达式[a-zA-Z0-9_]
。注意:缺省情况也包括unicode
文字字符,如果指定ASCII
码标记,则只包括ASCII
字母。示例:# coding : utf-8 # fun : 正则表达式demo:多行匹配 # @Author : Labyrinthine Leo # @Time : 2021.02.01 import re source = """ 王亚辉 tony 刘文典 """ p = re.compile(r'\w{2,4}',re.A) # re.A参数表示匹配ASCII字符 print(p.findall(source))
结果:
\W
匹配任意一个非文字字符,等价于表达式[^a-zA-Z0-9_]
[]
)方括号([]
)匹配单个字符:即匹配其中包含的字符,如[abc]
可以匹配abc中的任意一个字符;[a-z]
匹配范围内的字符;[^a-z]
表示非小写字母。
^
表示匹配文本的起始位置,正则表达式中可以设定单行模式和多行模式。如果是单行模式,表示匹配整个文本的开头位置;如果是多行模式,表示匹配文本每行的开头位置。
示例:
# coding : utf-8
# fun : 正则表达式demo:多行匹配
# @Author : Labyrinthine Leo
# @Time : 2021.02.01
import re
content = """
001-苹果价格-60,
002-橙子价格-70,
003-香蕉价格-80,
"""
# 欲匹配编号,如果使用'\d+'则会将价格也匹配到,所以要匹配开头位置的信息
p = re.compile(r'^\d+', re.M) # 添加开头信息标识符,M表示MULTILINE,多行模式
print(p.findall(content))
结果:
$
表示匹配文本的结束位置,单行多行同上,如\d+$
。
()
):组选择括号(()
)称之为正则表达式的组选择,即从正则表达式匹配的内容里面扣取出其中的某些需要的部分。
示例:
# coding : utf-8
# fun : 正则表达式demo:组选择
# @Author : Labyrinthine Leo
# @Time : 2021.02.01
import re
content = """
苹果,是绿色的
橙子,是橙色的
香蕉,是黄色的
乌鸦,是黑色的
"""
p = re.compile(r'^(.*),', re.M)
for i in p.findall(content):
print(i) # 打印的是组选择
结果:
注:如果多行匹配的多个组,则每一行都是一个组选择的元组
示例:
# coding : utf-8
# fun : 正则表达式demo:多行多个组选择
# @Author : Labyrinthine Leo
# @Time : 2021.02.01
import re
content = """
张三,手机号码15945678901
李四,手机号码13945677701
王二,手机号码13845666901
"""
p = re.compile(r'^(.+),.+(\d{11})', re.MULTILINE)
for one in p.findall(content):
print(one)
结果:
示例:
# coding : utf-8
# fun : 正则表达式demo:切割字符串
# @Author : Labyrinthine Leo
# @Time : 2021.02.01
import re
names = '关羽; 张飞, 赵云, 马超, 黄忠 李逵'
namelist = re.split(r'[;,\s]\s*', names)
print(namelist)
结果:
前面说过,点(.
)不能匹配换行符,如果我们使用时想让其匹配换行符,可以在compile()
方法中添加re.DOTALL
参数。
sub
方法进行替换示例:
# coding : utf-8 # fun : 正则表达式demo:sub方法模式替换 # @Author : Labyrinthine Leo # @Time : 2021.02.01 import re names = ''' 下面是这学期要学习的课程: 点击这里,边看视频讲解,边学习以下内容 这节讲的是牛顿第2运动定律 ''' newStr = re.sub(r'/av\d+?/', '/cn345677/', names) print(newStr)
结果:
示例:
# coding : utf-8 # fun : 正则表达式demo:指数函数模式替换 # @Author : Labyrinthine Leo # @Time : 2021.02.01 import re names = ''' 下面是这学期要学习的课程: 点击这里,边看视频讲解,边学习以下内容 这节讲的是牛顿第2运动定律 点击这里,边看视频讲解,边学习以下内容 这节讲的是毕达哥拉斯公式 点击这里,边看视频讲解,边学习以下内容 这节讲的是切割磁力线 ''' # 替换函数,参数是 Match对象 def subFunc(match): # Match对象 的 group(0) 返回的是整个匹配上的字符串 src = match.group(0) # Match对象 的 group(1) 返回的是第一个group分组的内容 number = int(match.group(1)) + 6 dest = f'/av{number}/' print(f'{src} 替换为 {dest}') # 返回值就是最终替换的字符串 return dest newStr = re.sub(r'/av(\d+?)/', subFunc , names) print(newStr)
结果:
临渊羡鱼不如退而结网
创作不易,如果您觉得这篇文章对你有用,可以点个赞,算是对笔者的支持和激励!这里是Leo的博客城堡,以Python为核,ML&DL为主,泛之形形色色,输寥寥拙见,摄浮光掠影,讲三两故事。临渊羡鱼,不如退而结网,持续干货输出,有趣的灵魂值得你的关注!
原文可以去笔者的github
主页:https://github.com/LabyrinthineLeo/Yxs_Git_Learning_repos
查看(如果可以,点个star
也无妨呀,嘿嘿)。