正则表达式
正则表达式入门
正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符")。
正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。
使用正则表达式 可以从一串杂乱无章的字符串中 获取你想要的内容 当然这些内容必须遵从一定的格式 而正则表达式就是 用他的语言描述这些格式。
案例:京东注册手机号校验
基本需求:手机号必须是11位、手机号必须以13 15 17 18 19开头、必须是纯数字
# python实现
while True:
# 1.获取用户输入的手机号
phone_num = input('请输入您的手机号>>>:').strip()
# 2.先判断是否是十一位
if len(phone_num) == 11:
# 3.再判断是否是纯数字
if phone_num.isdigit():
# 4.判断手机号的开头
if phone_num.startswith('13') or phone_num.startswith('15') or phone_num.startswith(
'17') or phone_num.startswith('18') or phone_num.startswith('19'):
print('手机号码输入正确')
else:
print('手机号开头不对')
else:
print('手机号必须是纯数字')
else:
print('手机号必须是11位')
# 正则表达式实现
import re
phone_number = input('please input your phone number: ')
if re.match('^(13|14|15|18)[0-9]{9}$', phone_number):
print('是合法的手机号码')
else:
print('不是合法的手机号码')
正则表达式格式
字符组
在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示
字符分为很多类,比如数字、字母、标点等等。
# 举例:[0123456789]
假如你现在要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、1、2...9这10个数之一。
用[0123456789]可以匹配0-9的任意数字 一次只匹配一个数字 简单写法[0-9]
ps:
# 1.字符组内所有的数据默认都是或的关系
# 2.字符组默认匹配方式是挨个挨个匹配
必须掌握的字符组
字符组 | 功能 |
---|---|
[0-9] | 匹配0到9任意一个数(缩写) |
[a-z] | 匹配26个小写英文字母 |
[A-Z] | 匹配26个大写英文字母 |
[0-9a-zA-Z] | 匹配数字或者小写字母或者大写字母 |
[0-9a-fA-F] | 可以匹配数字,大小写形式的a~f,用来验证十六进制字符 |
字符
每个正则字符都有一定功能 可以匹配对应的字符串
必须掌握的正则字符
正则字符 | 功能 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线(匹配变量名) |
\W | 匹配非数字、非字母、非下划线(\w的取反) |
\d | 匹配数字 |
^ | 匹配字符串的开头 |
$ | 匹配字符串的结尾 ^$两者组合使用可以非常精确的限制匹配的内容 |
a丨b | 匹配a或者b(管道符的意思是或) |
() | 给正则表达式分组 不影响表达式的匹配功能 |
[] | 字符组 内部填写的内容默认都是或的关系 |
[^] | 取反操作 匹配除了字符组里面的其他所有字符 |
\n | 匹配一个换行符 |
\s | 匹配任意的空白符 包括换行 空格 |
\t | 匹配一个制表符 |
\S | 匹配非空白符 |
\D | 匹配非数字 |
量词
- 量词不能单独使用
- 量词只作用与它前面的表达式 如果前面是一个组 则作用于这个组
miku\d{3} 量词{3}只影响\d
- 在默认情况下都是贪婪匹配 也就是尽可能的多匹配字符
必须要掌握的量词
量词 | 功能 |
---|---|
* | 匹配零次或多次 默认是多次(无穷次) |
+ | 匹配一次或多次 默认是多次(无穷次) |
? | 匹配零次或一次 作为量词意义不大主要用于非贪婪匹配 |
重复n次 | |
重复n次或更多次 默认是多次(无穷次) | |
重复n到m次 默认是m次 |
贪婪匹配与非贪婪匹配
- 可以通过在量词后面加?将贪婪匹配变为非贪婪匹配
- 非贪婪匹配的特性就是:尽可能的少匹配字符
- 量词默认都是贪婪匹配的 如{n,m}默认匹配m次 尽可能匹配多的字符
"""所有的量词都是贪婪匹配如果想要变为非贪婪匹配只需要在量词后面加问号"""
待匹配的文本
<script>alert(123)</script>
待使用的正则(贪婪匹配)
<.*>
请问匹配的内容
<script>alert(123)</script> 一条
# .*属于典型的贪婪匹配 使用它 结束条件一般在左右明确指定
待使用的正则(非贪婪匹配)
<.*?>
转义符补充
斜杠与字母的组合会产生特殊含义 斜杠也称为转义符
\n 匹配的是换行符
\\n 匹配的是文本\n
\\\\n 匹配的是文本\\n
ps:如果是在python中使用 还可以在字符串前面加r取消转义
re模块
re模块是python内置的正则表达式模块,需要做字符串匹配时可以考虑用。
常见方法
re.findall
语法:findall(正则表达式,待匹配字符串)
说明:findall会匹配字符串中所有符合的字符 并且将其存在一个列表里返回
import re
res = re.findall('a', 'a is apple aa is two apple')
print(res) # ['a', 'a', 'a', 'a', 'a'] # 匹配到了所有的a
re.finditer
说明:finditer跟findall只有一点不同,他不返回列表,而是返回一个迭代器对象
当待匹配的数据量很大时,考虑使用finditer =。=
import re
res = re.finditer('a', 'a is apple aa is two apple')
print(res) # <callable_iterator object at 0x000001F8A15986D0>
re.search
说明:search匹配到一个符合条件的数据就立刻结束 如果没有匹配到返回None search方法执行完会生成一个Match对象
# 1.基本使用
import re
res = re.search('a', 'a is apple aa is two apple')
print(res) # <re.Match object; span=(0, 1), match='a'>
res2 = re.search('a', 'is apple aa is two apple')
print(res2) # <re.Match object; span=(3, 4), match='a'>
# 2.group方法
import re
res = re.search('a[a-z]{3}e', 'a is apple aa is two apple')
print(res) # <re.Match object; span=(5, 10), match='apple'>
print(res.group()) # apple # group方法获取匹配到的值
re.match
说明:从字符串的开头开始匹配 如果开头不匹配后面不用看了 直接结束返回None
import re
res = re.match('apple', 'apple aa is two apple')
print(res) # <re.Match object; span=(0, 5), match='apple'>
print(res.group()) # apple
re.compile
说明:当一个正则表达式需要重复使用 可以考虑使用compile将其做成模板
import re
re_obj = re.compile('\d{3}') # 匹配三个数字
res1 = re_obj.findall('23423422342342344') # ['234', '234', '223', '423', '423']
res2 = re_obj.findall('asjdkasjdk32423') # ['324']
re.sub
语法:re.sub(正则表达式, 替换成的字符串, 目标字符串, 替换的次数)
说明:sub用于删除或替换一长串字符中的某些部分 返回经过处理的字符串
import re
ret = re.sub('\d', 'H', 'eva3hhh4yuan4', 1) # 将数字替换成'H',参数1表示只替换1个
print(ret) # evaHhhh4yuan4
ret = re.sub('\d', 'H', 'eva3hhh4yuan4')
print(ret) # evaHhhhHyuanH # 默认情况会全部替换
re.subn
说明: 返回一个元祖 包含替换处理后的字符串 和 替换了几次(替换次数)
import re
ret = re.subn('\d', 'h', '000000')
print(ret) # ('hhhhhh', 6)
re.split(少用)
import re
ret = re.split('[ab]', 'abcd') # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割
print(ret) # ['', '', 'cd']
补充说明
修饰符 re.S
可以用修饰符来控制匹配的模式。
在匹配网页时会出现经常出现换行符的情况,这时候点星问就不能匹配了,此时可以加一个修饰符re.S,来解决这个问题。
import re
text = '''www.baidu.com www.
4399.com'''
res = re.findall('www.(.*?).com', text)
print(res) # ['baidu']
res = re.findall('www.(.*?).com', text, re.S) # re.S使匹配内容包含换行符在内的所有字符
print(res) # ['baidu', '\n4399']
量词 | 功能 |
---|---|
re.S | 使匹配内容包含换行符在内的所有字符 |
re.I | 使匹配对大小写不敏感 |
re.L | 实现本地化识别(local-aware)匹配 |
re.M | 多行匹配,影响^和$ |
re.U | 根据Unicode字符集解析字符。这个标志会影响\w \W \b \B |
re.X | 该标志能够给予你更灵活的格式,以便将正则表达式书写的更易于理解 |
ps:在网页匹配中re.S re.I 比较常用
findall分组优先特性
说明:优先展示括号内正则表达式匹配到的内容
import re
res = re.findall('www.*?com', 'www.baidu.com www.4399.com') # 不分组的情况
print(res) # ['www.baidu.com', 'www.4399.com']
res2 = re.findall('www(.*?)com', 'www.baidu.com www.4399.com') # 括号内的为一组
print(res2) # ['.baidu.', '.4399.'] # 优先展示组内
取消分组优先特性 ?:
import re
res = re.findall('www(.*?)com', 'www.baidu.com www.4399.com') # 括号内的为一组
print(res) # ['.baidu.', '.4399.'] # 优先展示组内
res = re.findall('www(?:.*?)com', 'www.baidu.com www.4399.com') # 使用?:取消分组优先特性
print(res) # ['www.baidu.com', 'www.4399.com']
分组别名 和 group索引
说明:原括号内的为一组 组是可以命名的 也可以索引取值(group方法)
Match对象支持用名字取值 或者 是用索引取值(group方法)
import re
# 1.group索引取值
res = re.search('(www)(.*?)(com)', 'www.baidu.com')
print(res) # <re.Match object; span=(0, 13), match='www.baidu.com'> # match对象支持 索引取值
print(res.group()) # www.baidu.com
print(res.group(0)) # www.baidu.com
print(res.group(1)) # www
print(res.group(2)) # .baidu.
print(res.group(3)) # com
# 2.group用组名取值
res = re.search('(?P<url>www)(.*?)(com)', 'www.baidu.com') # ?P<url> 将这个组命名为url
print(res.group('url')) # www
练习
# 1.红牛分公司地址爬取 http://www.redbull.com.cn/about/branch 复制源代码在本地正则匹配
import re
with open('red bull.html', 'r', encoding='utf8') as f:
content = f.read()
res = re.findall("<li\n\s*data-longitude='(.*?)'\s*?data-latitude='(.*?)\s*'", content)
res2 = re.findall("<h2>(.*?)</h2>", content)
res3 = re.findall("<p class='mapIco'>(.*?)</p>", content)
red_bull = list(zip(res, res2, res3))
for one_campany in red_bull:
site, campany_name, location = one_campany
longitude, latitude = site
print(campany_name)
print(location)
print(f'经度:{longitude} 纬度:{latitude}')