likes
comments
collection
share

Spider理论系列-Re(正则表达式)

作者站长头像
站长
· 阅读数 13

正则是抓取网页数据的入门技能之一,正则可以全局通配你想要的数据,就比如:​​Spider实战系列-抓取《一人之下第三季》_浅辄的技术博客_51CTO博客​​这篇文章里,我想要通过源码来抓取url,但是xpath试过了抓不到,这时就可以使用正则表达式来抓取。

base_m3u8_url = re.search(

    '<iframe id="mplay" name="mplay" src="/js/player/?url=(.*?)" '

    'allowfullscreen="allowfullscreen" mozallowfullscreen="mozallowfullscreen" msallowfullscreen="msallowfullscreen" oallowfullscreen="oallowfullscreen" webkitallowfullscreen="webkitallowfullscreen" frameBorder="no" width="100%" scrolling="no" height="100%">',data).group(1)

m3u8_url = base_m3u8_url.split('&')[0]

一.正则基础

1.为什么使用正则

需求

判断一个字符串是否为手机号

解决

编写一个函数,给函数一个字符串,如果是手机号,则返回True,否则返回False

伪代码

def isPhone(phone):
    #长度为11
    #全部是数字字符
    #以1开头
    pass
if isPhone("13517925431"):
    print("是手机号")
else:
    print("不是手机号")

注意

如果使用正则会让这个问题变得简单

2.正则与re模块简介

概述: 正则表达式,又称规则表达式

正则表达式(regular expression)描述了一种字符串匹配的模式(pattern)

正则匹配是一个 模糊的匹配(不是精确匹配)

re: python自1.5版本开始增加了re模块,该模块提供了perl风格的正则表达式模式,re模块是python语言拥有了所有正则表达式的功能

  • 如下四个方法经常使用
  • match()
  • search()
  • findall()
  • finditer()

二、正则表达式

1、匹配单个字符与数字

匹配说明
.匹配除换行符以外的任意字符,当flags被设置为re.S时,可以匹配包含换行符以内的所有字符
[]里面是字符集合,匹配[]里任意一个字符
[0123456789]匹配任意一个数字字符
[0-9]匹配任意一个数字字符
[a-z]匹配任意一个小写英文字母字符
[A-Z]匹配任意一个大写英文字母字符
[A-Za-z]匹配任意一个英文字母字符
[A-Za-z0-9]匹配任意一个数字或英文字母字符
[^lee][]里的^称为脱字符,表示非,匹配不在[]内的任意一个字符
^[lee]以[]中内的某一个字符作为开头
\d匹配任意一个数字字符,相当于[0-9]
\D匹配任意一个非数字字符,相当于​​[^0-9]​
\w匹配字母、下划线、数字中的任意一个字符,相当于[0-9A-Za-z_]
\W匹配非字母、下划线、数字中的任意一个字符,相当于​​[^0-9A-Za-z_]​
\s匹配空白符(空格、换页、换行、回车、制表),相当于[ \f\n\r\t]
\S匹配非空白符(空格、换页、换行、回车、制表),相当于​​[^ \f\n\r\t]​

2、匹配锚字符

锚字符:用来判定是否按照规定开始或者结尾

匹配说明
^行首匹配,和[]里的^不是一个意思
$行尾匹配

^a匹配一个小写字母a必须作为开头

加上就是完全匹配

3、限定符

限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6种。

匹配说明
(xyz)匹配括号内的xyz,作为一个整体去匹配一个单元子存储
x?匹配0个或者1个x,非贪婪匹配
x*匹配0个或任意多个x
x+匹配至少一个x
x{n}确定匹配n个x,n是非负数
x{n,}至少匹配n个x
x{n,m}匹配至少n个最多m个x
xy表示或的意思,匹配x或y

不能单独使用需要配合正则表达式

三、re模块中常用函数

通用flags(修正符)

说明
re.I是匹配对大小写不敏感
re.S使.匹配包括换行符在内的所有字符

通用函数

  • 获取匹配结果
  • 使用group()方法 获取到匹配的值
  • groups() 返回一个包含所有小组字符串的元组(也就是自存储的值),从 1 到 所含的小组号。

1、match()函数

  • 原型
def match(pattern, string, flags=0)
  • 功能 匹配成功返回 匹配的对象 匹配失败 返回 None
  • 获取匹配结果
  • 使用group()方法 获取到匹配的值
  • groups() 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。
  • 注意:从第一位开始匹配 只匹配一次
  • 参数
参数说明
pattern匹配的正则表达式(一种字符串的模式)
string要匹配的字符串
flags标识位,用于控制正则表达式的匹配方式
  • 代码
import re

res = re.match('\d{2}','123')
print(res.group())

#给当前匹配到的结果起别名
s = '3G4HFD567'
re.match("(?P<value>\d+)",s)
print(x.group(0))
print(x.group('value'))

2、searce()函数

  • 原型
def search(pattern, string, flags=0)
  • 功能 扫描整个字符串string,并返回第一个pattern模式成功的匹配 匹配失败 返回 None
  • 参数
参数说明
pattern匹配的正则表达式(一种字符串的模式)
string要匹配的字符串
flags标识位,用于控制正则表达式的匹配方式
  • 注意: 只要字符串包含就可以 只匹配一次
  • 示例
import re

res = re.search('[a-z]', '131A3ab889s')
print(res)
print(res.group()
  • 注意 与search的区别 相同点: 都只匹配一次 不同点:
  • search是在要匹配的字符串中 包含正则表达式的内容就可以
  • match 必须第一位就开始匹配 否则匹配失败

3、findall()函数(返回列表)

  • 原型
def findall(pattern, string, flags=0)
  • 功能 扫描整个字符串string,并返回所有匹配的pattern模式结果的字符串列表
  • 参数
参数说明
pattern匹配的正则表达式(一种字符串的模式)
string要匹配的字符串
flags标识位,用于控制正则表达式的匹配方式
  • 示例
myStr = """
<a href="http://www.baidu.com">百度</a>
<A href="http://www.taobao.com">淘宝</A>
<a href="http://www.id97.com">电
影网站</a>
<i>我是倾斜1</i>
<i>我是倾斜2</i>
<em>我是倾斜2</em>
"""
# html里是不区分大小写
# (1)给正则里面匹配的 加上圆括号 会将括号里面的内容进行 单独的返回
res = re.findall("(<a href="http://www.(.*?).com">(.*?)</a>)",myStr) #[('<a href="http://www.baidu.com">百度</a>', 'baidu', '百度')]

# 括号的区别
res = re.findall("<a href="http://www..*?.com">.*?</a>",myStr) #['<a href="http://www.baidu.com">百度</a>']

#(2) 不区分大小写的匹配
res = re.findall("<a href="http://www..*?.com">.*?</a>",myStr,re.I) #['<a href="http://www.baidu.com">百度</a>', '<A href="http://www.taobao.com">淘宝</A>']
res = re.findall("<[aA] href="http://www..*?.com">.*?</[aA]>",myStr) #['<a href="http://www.baidu.com">百度</a>']
# (3) 使.支持换行匹配
res = re.findall("<a href="http://www..?.com">.?</a>",myStr,re.S) #
# (4) 支持换行 支持不区分大小写匹配
res = re.findall("<a href="http://www..?.com">.?</a>",myStr,re.S|re.I) #
print(res)

4、finditer()函数

  • 原型
def finditer(pattern, string, flags=0)
  • 功能 与findall()类似,返回一个迭代器
  • 参数
参数说明
pattern匹配的正则表达式(一种字符串的模式)
string要匹配的字符串
flags标识位,用于控制正则表达式的匹配方式
  • 代码
import re

res = re.finditer('\w', '12hsakda1')
print(res)
print(next(res))

for i in res:
    print(i)

5、split()函数

  • 作用:切割字符串
  • 原型:
def split(patter, string, maxsplit=0, flags=0)
  • 参数 pattern 正则表达式 string 要拆分的字符串 maxsplit 最大拆分次数 默认拆分全部 flags 修正符
  • 示例
import re
myStr = "asdas\rd&a\ts12d\n*a3sd@a_1sd"
#通过特殊字符 对其进行拆分 成列表
res = re.split("[^a-z]",myStr)
res = re.split("\W",myStr)

6、修正符

  • 作用 对正则进行修正
  • 使用 search/match/findall/finditer 等函数 flags参数的使用
  • 修正符 re.I 不区分大小写匹配 re.S 使.可以匹配换行符 匹配任意字符
  • 使用 re.I
print(re.findall('[a-z]','AaBb'))
print(re.findall('[a-z]','AaBb', flags=re.I))

re.S

print(re.findall('<b>.*?</b>','<b>b标签</b>'))
print(re.findall('<b>.*?</b>','<b>b标\n签</b>', flags=re.S))

四、正则高级

1、分组&起名称

  • 概念 处理简单的判断是否匹配之外,正则表达式还有提取子串的功能,用()表示的就是要提取的分组
  • 代码
#给当前匹配到的结果起别名
s = '3G4HFD567'
re.match("(?P<value>\d+)",s)
print(x.group(0))
print(x.group('value'))
  • 说明
  • 正则表达式中定义了组,就可以在Match对象上用group()方法提取出子串来
  • group(0)永远是原始字符串,group(1)、group(2)……表示第1、2、……个子串

2、编译

  • 概念 当在python中使用正则表达式时,re模块会做两件事,一件是编译正则表达式,如果表达式的字符串本身不合法,会报错。另一件是用编译好的正则表达式提取匹配字符串
  • 编译优点 如果一个正则表达式要使用几千遍,每一次都会编译,出于效率的考虑进行正则表达式的编译,就不需要每次都编译了,节省了编译的时间,从而提升效率
  • compile()函数
  • 原型
def compile(pattern, flags=0)
  • 作用 将pattern模式编译成正则对象
  • 参数
参数说明
pattern匹配的正则表达式(一种字符串的模式)
flags标识位,用于控制正则表达式的匹配方式
  • flags
说明
re.I是匹配对大小写不敏感
re.S使.匹配包括换行符在内的所有字符
  • 返回值 编译好的正则对象
  • 示例
import re

re_phone = re.compile(r"(0\d{2,3}-\d{7,8})")
print(re_phone, type(re_phone))
  • 编译后其他方法的使用 原型
def match(self, string, pos=0, endpos=-1)
def search(self, string, pos=0, endpos=-1)
def findall(self, string, pos=0, endpos=-1)
def finditer(self, string, pos=0, endpos=-1)

参数

参数说明
string待匹配的字符串
pos从string字符串pos下标开始
endpos结束下标

示例

s1 = "lucky's phone is 010-88888888"
s2 = "kaige's phone is 010-99999999"
ret1 = re_phone.search(s1)
print(ret1, ret1.group(1))
ret2 = re_phone.search(s2)
print(ret2, ret2.group(1))

3、贪婪与非贪婪(重点)

  • 贪婪模式 贪婪概念:匹配尽可能多的字符
  • .+ 匹配换行符以外的字符至少一次
  • .* 匹配换行符以外的字符任意次

实例

res = re.search('<b>.+</b>', '<b></b><b>b标签</b>')
res = re.search('<b>.*</b>', '<b>b标签</b><b>b标签</b><b>b标签</b><b>b标签</b>')
  • 非贪婪模式 非贪婪概念:尽可能少的匹配称为非贪婪匹配,*?、+?即可
  • .+? 匹配换行符以外的字符至少一次 拒绝贪婪
  • .*? 匹配换行符以外的字符任意次 拒绝贪婪

实例

res = re.search('<b>.+?</b>', '<b>b标签</b><b>b标签</b>')
res = re.search('<b>.*?</b>', '<b>b标签</b><b>b标签</b><b>b标签</b><b>b标签</b>')

写在最后

其实正则用了这么久,最常用的还是非贪婪模式的(.*?)