python爬虫之正则表达式(一)

1 正则表达式的简介

1.1 概念

正则表达式是对字符串操作的⼀种逻辑公式,就是用事先定义好的⼀些特定字符、及这些特定字符的组合,组成⼀个 “ 规则字符串 ” ,这个 “ 规则字符串 ” 用来表达对字符串的⼀种过滤逻辑。
例如:有一把秤,专门用来筛选1斤的东西。现在有一些东西,重量分别由1、2、3、4斤,用这把秤就能够筛选出1斤的东西。而这把秤就可以理解为正则表达式。

1.2 正则表达式的应用场景

  • 表单验证 ( 例如手机号、邮箱、身份证 … )
  • 爬虫

2 正则表达式对 Python 的支持

2.1 普通字符

字母、数字、汉字、下划线、以及没有特殊定义的符号,都是 " 普通字符 " 。正则表达式中的普通字符,在匹配的时候 , 只匹配与自身相同的⼀个字符

2.2 match()函数和search()函数

match(pattern, string, flags=0)
- 第⼀个参数是正则表达式,如果匹配成功,则返回⼀个 match 对象,否则返回⼀个 None
- 第二个参数表示要匹配的字符串
- 第三个参数是标致位⽤于控制正则表达式的匹配⽅式 如 : 是否区分大小写, 多行匹配等等
- 查找顺序为第一个开始匹配,若第一个匹配不正确,则返回为None
- 
search(pattern, string, flags=0)
- 第⼀个参数是正则表达式,如果匹配成功,则返回⼀个 search 对象,否则返回⼀个 None
- 第二个参数表示要匹配的字符串
- 第三个参数是标致位⽤于控制正则表达式的匹配⽅式 如 : 是否区分大小写, 多行匹配等等
- 查找顺序为全部内容匹配,若内容存在有匹配,则返回匹配对象

例如:正则表达式 c ,在匹配字符串 abcde 时,匹配结果是:成功;匹配到的内容是 c ;匹配到的位置开始于 2 ,结束于 3 。

import re  # 导入正则表达式模块
# 正则表达式
pattern = 'c'
s = 'abcde'
result1 = re.match(pattern,s)  # match方法是从第一个字符串开始匹配,若无匹配则为None
result2 = re.search(pattern,s)  # search方法是全部匹配匹配,若无匹配则也为None
print(result1,result2)  # result1 = None result2 = <re.Match object; span=(2, 3), match='c'>

2.3 元字符

正则表达式中使用了很多元字符,用来表示⼀些特殊的含义或功能:
在这里插入图片描述

import re

# . 可以匹配除了换行之外的任意一个字符
In[3]: re.match(r'a.c','abc').group()  # 结果为 abc
In[4]: re.match(r'a.c','aHb').group()  # 结果为 报错,匹配不到  AttributeError: 'NoneType' object has no attribute 'group'
In[5]: re.match(r'a.c','aHc').group()
In[6]: re.match(r'a.c','a2c').group()  # 结果为 a2c
In[7]: re.match(r'a.c','a\nc').group() # 结果为 报错 无法识别换行符  AttributeError: 'NoneType' object has no attribute 'group'
In[8]: re.match(r'a.c','a你c').group() # 结果为 a你c
In[9]: re.match(r'a.c','a你好c').group() # 结果为 报错 只能识别一个字符  AttributeError: 'NoneType' object has no attribute 'group'

# / 等同于逻辑或操作符
In[11]: re.match(r'a|c','c').group() # 结果为 'c'
In[12]: re.match(r'a|c','b').group() # 结果为 报错
In[13]: re.match(r'a|b','ab').group() # 结果为 'a'
In[14]: re.match(r'a|b','ba').group() # 结果为 'b'
In[15]: re.match(r'a|b','cba').group() # 结果为 报错  match()从第一个开始匹配,第一个匹配无,则print None
In[16]: re.search(r'a|b','cba').group() # 结果为 'b'

# [] 匹配字符集中的一个字符
In[18]: re.match(r'[abc]','a').group() # 结果为 'a'
In[19]: re.match(r'[abc]2','a').group() # 结果为 报错 'NoneType' object has no attribute 'group'
In[20]: re.match(r'速度与激情[12345678]','速度与激情6').group()  # 结果为 速度与激情6
In[21]: re.match(r'速度与激情[12345678]','速度与激情9').group() # 结果为 报错
In[22]: re.match(r'速度与激情[12345678]','速度与激情12').group() # 结果为 速度与激情1   [] 匹配字符集中的一个字符

# ^ 对字符集的求反,也就是上面的反操作,尖括号必须在方括号的最前面
In[24]:re.match(r'速度与激情[^12345678]','速度与激情0').group() # 结果为 速度与激情0
In[25]: re.match(r'速度与激情[^12345678]','速度与激情9').group() # 结果为 速度与激情9
In[26]: re.match(r'速度与激情[^12345678]','速度与激情10').group() # 结果为 报错

# 匹配字符集中的一个字符,[]等同于区间
In[27]: re.match(r'速度与激情[1-8]','速度与激情0').group()
In[28]: re.match(r'速度与激情[1-8]','速度与激情6').group() # 结果为 速度与激情6
In[29]: re.match(r'速度与激情[1-8a-z]','速度与激情6a').group() # 结果为 速度与激情6
In[30]: re.match(r'速度与激情[a-z]','速度与激情a').group() # 结果为 速度与激情a

⼀些无法书写或者具有特殊功能的字符,采用在前面添加反斜杠 “\” 进行转义
在这里插入图片描述

import re
# \ 对紧跟其后的一个字符进行转义
In[32]: re.match(r'速度.与激情[a-z]','速度.与激情a').group() # 结果为 速度.与激情a
In[33]: re.match(r'速度\.与激情[a-z]','速度.与激情a').group() # 结果为 速度.与激情a
In[34]: re.match(r'速度.与激情[a-z]','速度a与激情a').group() # 结果为 速度a与激情a
In[35]: re.match(r'速度\.与激情[a-z]','速度a与激情a').group()  # 无法匹配

2.4 预定义匹配字符集

正则表达式中的⼀些表示方法,可以同时匹配某个预定义字符集中的任意⼀个字符。
如,表达式 \d 可以匹配任意⼀个数字。虽然可以匹配其中任意字符,但是只能是⼀个,不是多个
在这里插入图片描述

import re
# \d 0-9中任意一个数字
In[37]: re.match(r'123','123').group()  # 结果为 123
In[38]: re.match(r'[1]23','123').group() #  结果为 123
In[39]: re.match(r'[1-3]','123').group() #  结果为 1
In[40]: re.match(r'\d','123').group() #  结果为 123

# \w 任意一个字母或数字或下划线
In[42]: re.match(r'\w','a123').group() # 结果为 a123
In[43]: re.match(r'[a-zA-Z0-9_]','a123').group()  # 等同于

# \s 空格 制表符 换页符 等空白字符中的任意一个
In[47]: re.match(r'\s',' ').group() # 结果为空格
In[48]: re.match(r'\s','\t').group()  # 结果为 \t
In[50]: re.match(r'速度与激情\d','速度与激情7').group()  # 结果为 速度与激情7
In[51]: re.match(r'速度与激情\w','速度与激情A').group() # 结果为 速度与激情A

# \D \d的取反操作 也就是非数字的任意一个字符 等同于[^\d]
In[57]: re.match(r'速度与激情\D','速度与激情_').group() # 结果为 速度与激情_
In[58]: re.match(r'速度与激情\D','速度与激情&').group()  # 结果为 速度与激情&

2.5 重复匹配

前⾯的表达式,无论是只能匹配⼀种字符的表达式,还是可以匹配多种字符其中任意⼀个的表达式,都只能匹配⼀次。但是有时候我们需要对某个字段进行重复匹配,例如手机号码 13666666666 ,⼀般的新手可能会写成\d\d\d\d\d\d\d\d\d\d\d 。
注意,这不是⼀个恰当的表达,不但写着费劲,看着也累,还不⼀定准确恰当。
这种情况可以使用表达式再加上==修饰匹配次数的特殊符号 {} ==,不但重复书写表达式就可以重复匹配。例如 [abcd][abcd] 可以写成 [abcd]{2}
在这里插入图片描述

import re
# {n} 表达式重复n次
In[63]: re.match(r'\d{3}','123').group() # 结果为 123
In[64]: re.match(r'\d{11}','12345678901').group() # 结果为 12345678901

# {m,n} 表达式至少重复m次 最多重复n此
In[66]: re.match(r'\d{4}-\d{7}','0123-1234567').group() 结果为 0123-1234567

# {m,} 表示至少重复m次
In[72]: re.match(r'\d{3,}-\d{7,8}','012-12345678').group()  结果为 0123-1234567
In[73]: re.match(r'\d{3,}-\d{7,8}','0132-12345678').group()   结果为 0132-12345678

# ?表示前一个字符出现0次或者1次 要么有1次要么没有
In[77]: re.match(r'w[a-z]?','wdeco').group()  # 结果为w
In[78]: re.match(r'w[a-z]?','w').group()  # 结果为w

# + 匹配前一个字符出现1次或者无限次,至少1次
In[81]: re.match(r'w[a-z]+','wdeco').group() # 结果为 wdeco
In[82]: re.match(r'w[a-z]+','wd').group() # 结果为 wd

# * 出现0次到任意次
In[87]: h = '''afdsfdsfd
fdfdfdfd
fdfdfdfdfdf
fdfdfdfdf
fdfdfdfdfdffdf
'''
In[88]: re.match('.*',h).group()
In[89]: re.match('.*',h,re.S).group() # S标识为识别换行,所有内容

2.6 位置匹配和非贪婪匹配

2.6.1 位置匹配

有时候,我们对匹配出现的位置有要求,比如开头、结尾、单词之间等等,这时候可以用到位置匹配。常用的位置匹配如下:
在这里插入图片描述

import re
print(re.match(r'abc','abcdef').group())  # 结果为abc

# ^:在字符串开始匹配的地方,符号本身不匹配任何的字符,要求匹配开始的内容是什么
print(re.match(r'^abc','abcdef').group())  # 要求字符开始匹配的地方为a,结果为abc
# print(re.match(r'^abc','eabcdef').group())  # 开始不是'abc'报错:AttributeError: 'NoneType' object has no attribute 'group'
print(re.match(r'^[\d]w','1wbcdef').group()) # 要求字符开始匹配的地方为数字

# $:在字符串结束的地方匹配 符合本身不匹配任何的字符,要求匹配最后的内容是什么
print(re.match(r'[\w]+@qq.com$','9948@qq.com'))  # <re.Match object; span=(0, 11), match='9948@qq.com'>
print(re.match(r'[\w]+@qq.com$','9948@qq.com.cn'))  # None,尾端为.cn,不符合qq.com
print(re.match(r'a.c$','abc')) # <re.Match object; span=(0, 3), match='abc'>

# \b:表示字母数字与非字母数字的边界,非字母数字与字母数字的边界。符号本身不匹配任何内容
print(re.split('123\\b','==123!! abc123. 123. 123abc. 123'))  #['==', '!! abc', '. ', '. 123abc. ', '']
# 数字与非字母数字的边界;同时看到'123abc'没有被分隔,符合\b的意思。
print(re.split('123\\b','==123!! abc123. 123\tabc 123'))  # ['==', '!! abc', '. ', '\tabc ', '']
print(re.split('\\b123\\b','123 ==123!! abc123.123.123abc.123'))  # 看到是四对分隔边界,分别对应123前后的\b。
# ['', ' ==', '!! abc123.', '.123abc.', '']
print(re.split('\\b123=\\b','==123!! abc123,123,123=abc,123')) #['==123!! abc123,123,', 'abc,123']
# 只有一对(一处)分隔边界 前面表示字母数字与数字的边界,后面表示非字母数字与字母的边界。
print(re.split('\\b123=\\b','==123!! abc123,123,123==abc,123')) # ['==123!! abc123,123,123==abc,123']
# 可以看到字符串没有被分隔,是因为箭头处的边界表示非字母数字与非字母数字的边界,不是\b的意思

# \B:表示字母数字与(非非)字母数字的边界,非字母数字与非字母数字的边界
print(re.split(r'pyc\B','1pycthon py5 2pyc342 pyc1py2py4 pyp3 3pyc# pyc')) # ['1', 'thon py5 2', '342 ', '1py2py4 pyp3 3pyc# pyc']
# 表示非字母数字与非字母数字的边界,'1py=cthon','2py=342'没有被分隔,是因为非字母数字与字母数字的边界。
print(re.split('\\b123=\\B','==123!! abc123,123,123==abc,123')) # ['==123!! abc123,123,', '=abc,123']
# 有一对(一处)分隔边界,前面箭头处表示非字母数字与数字的边界,后面箭头处表示非字母数字与非字母数字的边界。都符合\b,\B的意思。

\b和\B的学习参考自:正则表达式里\b和\B,Python实例

2.6.2 贪婪和非贪婪匹配

在重复匹配时,正则表达式默认总是尽可能多的匹配,这被称为贪婪模式。
例如,针对文本dxxxdxxxd ,表达式 (d)(\w+)(d) 中的 \w+ 将匹配第⼀个 d 和最后⼀个 d 之间的所有字符 xxxdxxx 。

print(re.match(r'd(\w+)d','dxxxdxxxd').group()) # 贪婪模式

可见, \w+ 在匹配的时候,总是尽可能多的匹配。
符合它规则的字符。同理,带有 ? 、 * 和 {m,n} 的重复匹配表达式都是尽可能地多匹配

print(re.match(r'<div>.*</div>','<div>abc</div><div>bcd</div>').group()) # 贪婪 <div>abc</div><div>bcd</div>
# 结果全部匹配,表现出python中总是尽可能多地匹配结果 * 出现0次到任意次
print(re.match(r'<div>.*?</div>','<div>abc</div><div>bcd</div>').group()) # <div>abc</div>
#  ?表示前一个字符出现0次或者1次 要么有1次要么没有

2.7其他场景的表达式

2.7.1 校验数字的相关表达式

在这里插入图片描述

import re
# 数字 :^[0-9]*$,其中^为在字符串开始匹配的地方,[0-9]为0-9的字符集,*为出现0次或任意次,$为在字符串结束匹配的地方
print(re.match(r'^[0-9]*$','1234567'))  # <re.Match object; span=(0, 7), match='1234567'>

# n位的数字: ^\d{n}$
n = input('请输入位数:')
print(re.match(r'^\d{n}$','123456')) # 3,None

# 至少n位的数字: ^\d{n,}$
n = input('请输入位数:')
print(re.match(r'^\d{3,}$','123456')) # 3,<re.Match object; span=(0, 6), match='123456'>

# 零和非零开头的数字: ^(0|[0-9]*)$
print(re.match(r'^(0|[0-9]*)$','0123')) # <re.Match object; span=(0, 4), match='0123'>

# 有两位小数的正实数: ^[0-9]+(.[0-9]{2})+$
print(re.match(r'^[0-9]+(.[0-9]{2})$','2.00')) # <re.Match object; span=(0, 4), match='2.00'>
print(re.match(r'^[0-9]+(.[0-9]{2})$','2.125')) # None

# 浮点数:^(-?\d+)(\.\d+)?$                   ?表示前一个字符出现0次或者1次 要么有1次要么没有
print(re.match(r'^(-?\d+)(\.\d+)?$','2.1100')) # None

2.7.2 特殊场景的表达式

在这里插入图片描述
特殊的应用场景可以阅读这一篇博文:写得很赞
正则表达式的常用场景

'''
正则表达式的应用场景
'''
import re
# 密码强度设置
key = input('请输入您的密码:')
if re.match(r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*[0-9])(?=.*[!@#$%^&*()-]).{6,}$',key):
    print('密码强度强')  # 最少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符
else:
    print('密码强度为弱')
# 邮箱
 re.match(r'^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$', "153522217@qq.com")

# 身份证
re.match(r'^[1-9]\d{5}(18|19|[23]\d)\d{2}[0-1][0-9][0-3][0-9]\d{3}[0-9Xx]$','12345619900909889x')
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值