day2 正则表达式
1. 正则的作用
正则表达式是一种可以让复杂的字符串变得简单的工具
写正则表达式时就是用正则表达式来描述字符串规则
先导再用:
from re import *
2. 正则的符号
**注意:Python中表达式一个正则表达式一般使用 r字符串 **
from re import fullmatch, findall, search
1)匹配类符号
a. 普通符号 - 在正则中表示其本身的符号
result = fullmatch(r'abc', 'abc')
# fullmatch匹配失败则为空
print(result) # <re.Match object; span=(0, 3), match='abc'> 是匹配对象
result1 = fullmatch(r'abc', 'cbc')
print(result1) # 匹配失败,为None
b. . - 匹配任意一个字符
result2 = fullmatch(r'.bc', ',bc') # 表3个字符,bc结尾, .代表任意一个字符都可以
result3 = fullmatch(r'.b.', ' b/')
# 都匹配成功
c. \d - 匹配任意一个数字字符
result4 = fullmatch(r'\d\dabc', '68abc')
# 匹配成功
d. **\s ** - 匹配任意一个空格字符: ‘\n’、‘\t’、’ '. 直接按tab键不行,因为tab键时四个字符
# 空白字符:空格(' ')、换行('\n')、水平制表符('\t')
result = fullmatch(r'123\sabc', '123\nabc')
# 匹配成功
e. \w - 匹配任意一个字母、数字、下划线、中文字符
result5 = fullmatch(r'abc\w123', 'abc哈123')
# 匹配成功
f. \D、\S、\W 分别和 \d、\s、\w 的功能相反
\D — 匹配任意一个非数字字符。
\S — 匹配任意一个非空格字符。
\W — 匹配任意一个非字母、数字、下划线、中文的字符。
result6 = fullmatch(r'123\Dabc', '1234abc')
print(result6) # 匹配失败为None
result6 = fullmatch(r'123\Sabc', '123 abc')
print(result6) # 匹配失败为None
result6 = fullmatch(r'123\Wabc', '123*abc')
print(result6) # 匹配成功
result6 = fullmatch(r'123\Wabc', '1234abc')
print(result6) # 匹配失败为None
g. [字符集] - 匹配在字符集中的任意一个字符
[abc] — 匹配 a 或者 b 或者 c
[abc\d] — 匹配 a 或者 b 或者 c 或者 任意一个数字
[1-5] — 匹配字符1到字符5的任意一个字符(按编码值顺序)
[a-z] — 匹配任意一个字符小写字母
[a-zA-Z] — 匹配任意一个字母
[a-zA-Z\d] — 匹配任意一个字母或数字
[a-zA-Z=%] — 匹配任意一个字母或 = 或 %
[\u4e00-\u9fa5] – 匹配任意一个中文
r = fullmatch(r'abc[M9你]123', 'abc你123')
# abc和123之间的字符只能是m 或9 或你
r1 = fullmatch(r'abc[\u4e00-\u9fa5]', 'abc猜')
# 匹配成功
h. [^字符集] — 与[]相反,即匹配不在字符集中的任意字符; ^必须放在字符集前面
r3 = fullmatch(r'abc[MN]123', 'abcM123')
r4 = fullmatch(r'abc[^MN]123', 'abcM123')
print(r3) # 匹配成功
print(r4) # 匹配失败
2)匹配次数符号
-
***** — 任意次数(0次、1次、多次)
a* --- a出现任意多次 \d* --- 任意多个任意数字 ''' result7 = fullmatch(r'1a*2','12') # 匹配成功 result8 = fullmatch(r'M\d*N', 'M238N') # 匹配成功
-
+ — 至少一次
-
? — 至多一次
-
{M} — M次
- {M,N} — M到N次(小于N)
- {M,} ---- 最少M次
- {,N} — 最多N次
# 练习:写一个正则表达式,可以匹配任意一个除了0的整数。 # 合法:233、+234、-7283、100、-2000 # 不合法:0、0002、2.23 result9 = fullmatch(r'[+-]?[1-9]{1}\d*', '57530864.9998') print(result9)
-
贪婪模式和非贪婪模式
在匹配次数不确定时,如果 !!!(前提:) 有多种次数都可以匹配成功 !!!
贪婪会取次数最多的那个次数来匹配
非贪婪会取最少的次数来匹配默认就是贪婪模式
非贪婪模式:在所有匹配次数不确定的符号后面再加一个问号
贪婪模式:+、?、、{M,N}、{M,}、{,N}
非贪婪模式:+?、??、?、{M,N}?、{M,}?、{,N}?# 贪婪 t = search(r'a.+b', 'kkkahkmbxyb哈哈bkkhh讲解') print(t) # <re.Match object; span=(3, 14), match='ahkmbxyb哈哈b'> # 非贪婪 ft = search(r'a.+?b', 'kkkahkmbxyb哈哈bkkhh讲解') print(ft) # <re.Match object; span=(3, 8), match='ahkmb'> # 如果提供的字符串只有一种情况满足匹配,那么贪婪和非贪婪都一样
3)匹配分组和分支
分组
正则表达式中可以用()将部分内容括起来表示一个整体;括号括起来的部分就是一个分组
a. 整体操作的时候需要分组.
如能匹配两数字一个大写字母两个数字一个大写字母这种规律的所有字符串的正则表达式
'23M','89K54F','45F23S89N67G'…同时匹配这些字符串
b. 重复匹配 — 正则中可以通过 \M 来重复它前面第M个分组匹配的结果。(从前往后数到\M第M个分组,其后的分组不考虑)
‘23M23’,‘90K90’,……一个大写字母前时任意两个数字,大写字母后两个数字必须和前面相同
c. 捕获 — 提取分组匹配到的结果(捕获分为自动捕获(findall)和手动捕获)
# '23M','89K54F','45F23S89N67G'…同时匹配这些字符串
result = fullmatch(r'(\d\d[A-Z])+', '67G')
print(result)
# '23M23','90K90'
result = fullmatch(r'(\d\d)[A-Z]\1', '12M12')
print(result)
自动捕获
# 捕获:只有findall有自动捕获的功能
message = '技术上789计算机系那你jkkk 900nnn999睡觉哦234酷酷酷553kd'
# 提取中文后的数字
result = findall(r'[\u4e00-\u9fa5](\d+)', message)
print(result)
# r'[\u4e00-\u9fa5](\d+)'是筛选满足条件的字符串,对其中需要获取的内容加括号即可获取
手动捕获
message = '我是小明, 今年23岁, 身高180厘米, 体重70kg'
result = search(r'身高\d+', message)
print(result) # 得到匹配对象:<re.Match object; span=(13, 18), match='身高180'>
# 在匹配对象中获取想要的结果
# 匹配对象.group(N) --- 获取匹配结果中指定分组匹配到的内容
print(result.group()) # 身高180
result = search(r'身高(\d+)', message)
print(result.group(1)) # 180
result = search(r'身高(\d+)厘米, 体重(\d+)kg', message)
print(result.group(1), result.group(2)) # 180 70
分支
正则1|正则2|正则3|…… — 先用正则1进行匹配,匹配成功直接成功;匹配失败用正则2进行匹配, 以此类推
result = fullmatch(r'\d{3}|[a-z]{2}','123')
print(result)
result = fullmatch(r'\d{3}|[a-z]{2}','mn')
print(result)
# abc两个数字 或 abc两个大写字母
result = fullmatch(r'abc\d\d|abc[A-Z]{2}','abcFG')
print(result)
# 优化:将需要分支的地方括起来
result = fullmatch(r'abc(\d\d|[A-Z]{2})','abcFG')
print(result)
4) 转移符号
转义符号:在本身具有特殊功能或者特殊意义的符号前加\,让特殊符号变成普通符号
匹配整数部分和小数部分都是两位数的小数。 .不能直接写入正则表达式,因为.匹配任意一个字符
为了让.就代表它本身,则在其之前加\ ---- \.
result = fullmatch(r'\d\d\.\d\d','45.78')
print(result)
# 同理还有 + 、()、 ---- \+ 、 \( \)
result = fullmatch(r'\d\d\+\d\d','45+78')
print(result)
result = fullmatch(r'\(\d\d\,\d\d\)','(45,78)')
print(result)
# 注意:单独存在有特殊意义的符号,在[]中它的功能会自动消失
# 如:+ . ?() *
# 但反斜杠开头的或者[]在[]中功能不会消失:\d、[]
3. re模块
from re import fullmatch, findall, search, split,sub,finditer,match
1)fullmatch(正则,字符串) — 用整个字符串和正则,匹配成功返回匹配对象,匹配返回None
2)findall(正则,字符串) — 获取字符串中所有满足正则的子串,默认返回一个列表,列表中元素是所有匹配到的子串
存在自动捕获现象
3)search(正则,字符串) — 匹配第一个满足正则的子串,匹配成功返回匹配对象,匹配返回None
4)split(正则,字符串,[N]) — 将字符串中所以满足正则的子串作为切割点进行切割,
N表示在指定字符串前N个指定切割点进行切割。[N]表示N参数可有可无
5)sub(正则,字符串1,字符串2) — 将字符串2中所有满足正则的子串都替换为字符串1
6)finditer(正则,字符串) — 获取字符串中所有满足正则的子串,获取一个迭代器,迭代器的元素是匹配对象
7)match(正则,字符串) — 若字符串开头的子串满足正则则匹配成功
str1 = 'jdjnnxj7jsnjj7kkkk7'
str1.split('7',2)
# 字符串切割 --- 针对某一个字符作为切割点
str1 = '技术22竞技赛科学家74不不不3健康'
print(split(r'\d+',str1,2)) # ['技术', '竞技赛科学家', '不不不3健康']
# 满足正则的子串作为切割点
print(sub(r'\d+','+', str1)) # 技术+竞技赛科学家+不不不+健康
message = '傻逼,sb,都打起来了你还在打野!s b'
print(sub(r'sb|傻逼|s\s*b','*',message)) # *,*,都打起来了你还在打野!*
result = finditer(r'\d+',str1)
print(list(result))
# 当想要在正则中加括号,但不想用分组功能时就用finditer
print(match(r'\d{3}', '234tgfd'))
# 匹配成功
补充
1)忽略大小写 — 在正则表达式中最前面加 (?i)
print(fullmatch(r'(?i)abc','Abc'))
2)单行匹配:(?s)
多行匹配(默认): . 不能和换行符进行匹配
单行匹配: . 可以和换行符进行匹配
print(fullmatch(r'abc.123','abc\n123')) # None
print(fullmatch(r'(?s)abc.123','abc\n123')) # 匹配成功
"""
针对爬虫时由于需要的数据太长而分了多行显示,则提取时用 . 只能提取到第一行,此时则需要用单行匹配
'name':"isidn-k99k
uxjkooa"
"""
作业
爬取豆瓣网页信息
import requests
from re import findall,fullmatch,split
# 用正则表达式获取豆瓣电影top250第一页的网页信息
headers ={
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36'
}
response = requests.get('https://movie.douban.com/top250?start=0&filter=',headers=headers)
result = response.text
# print(result)
# 获取电影名称
movies_name = findall(r'<span class="title">([\u4e00-\u9fa5]+?)</span>',result)
# 获取导演名称
director = findall(r'导演: ([\u4e00-\u9fa5]+·?[\u4e00-\u9fa5]*)', result)
# 获取时间
time = findall(r'...<br>\s*(\d{4}) / ',result)
# 获取国家:
country = findall(r'...<br>\s*\d{4} / ([\u4e00-\u9fa5]+\s*[\u4e00-\u9fa5]+)',result)
# 获取电影类型
movie_types = findall(r'[\u4e00-\u9fa5]+ / ([\u4e00-\u9fa5\s]+[\u4e00-\u9fa5]+)+\s*</p>',result)
# 获取评分
score = findall(r'property="v:average">(\d+\.?\d*)</span>',result)
# 获取评价人数
count = findall(r'</span>\s*<span>(\d+人评价)</span>\s*</div>',result)
# 获取标志词
word = findall(r'<span class="inq">([\u4e00-\u9fa5]+[,·\u4e00-\u9fa5]*。?)\s*</span>',result)
douban_message =[]
for i in range(25):
data1 = {}
data1['name'] = movies_name[i]
data1['director'] = director[i]
data1['time'] = time[i]
data1['country'] = country[i]
data1['movie_types'] = movie_types[i]
data1['score'] = score[i]
data1['count'] = count[i]
data1['word'] = word[i]
douban_message.append(data1)
print(douban_message)
最后得到的字典数据:
[{'name': '肖申克的救赎', 'director': '弗兰克·德拉邦特', 'time': '1994', 'country': '美国', 'movie_types': '犯罪 剧情', 'score': '9.7', 'count': '2820758人评价', 'word': '希望让人自由。'}, {'name': '霸王别姬', 'director': '陈凯歌', 'time': '1993', 'country': '中国大陆 中国香港', 'movie_types': '剧情 爱情 同性', 'score': '9.6', 'count': '2086339人评价', 'word': '风华绝代。'}, {'name': '阿甘正传', 'director': '罗伯特·泽米吉斯', 'time': '1994', 'country': '美国', 'movie_types': '剧情 爱情', 'score': '9.5', 'count': '2112141人评价', 'word': '一部美国近现代史。'}, {'name': '泰坦尼克号', 'director': '詹姆斯·卡梅隆', 'time': '1997', 'country': '美国 墨西哥', 'movie_types': '剧情 爱情 灾难', 'score': '9.5', 'count': '2076216人评价', 'word': '失去的才是永恒的。'}, {'name': '这个杀手不太冷', 'director': '吕克·贝松', 'time': '1994', 'country': '法国 美国', 'movie_types': '剧情 动作 犯罪', 'score': '9.4', 'count': '2246629人评价', 'word': '怪蜀黍和小萝莉不得不说的故事。'}, {'name': '美丽人生', 'director': '罗伯托·贝尼尼', 'time': '1997', 'country': '意大利', 'movie_types': '剧情 喜剧 爱情 战争', 'score': '9.6', 'count': '1298195人评价', 'word': '最美的谎言。'}, {'name': '千与千寻', 'director': '宫崎骏', 'time': '2001', 'country': '日本', 'movie_types': '剧情 动画 奇幻', 'score': '9.4', 'count': '2188676人评价', 'word': '最好的宫崎骏,最好的久石让。'}, {'name': '辛德勒的名单', 'director': '史蒂文·斯皮尔伯格', 'time': '1993', 'country': '美国', 'movie_types': '剧情 历史 战争', 'score': '9.6', 'count': '1081401人评价', 'word': '拯救一个人,就是拯救整个世界。'}, {'name': '盗梦空间', 'director': '克里斯托弗·诺兰', 'time': '2010', 'country': '美国 英国', 'movie_types': '剧情 科幻 悬疑 冒险', 'score': '9.4', 'count': '2017631人评价', 'word': '诺兰给了我们一场无法盗取的梦。'}, {'name': '星际穿越', 'director': '克里斯托弗·诺兰', 'time': '2014', 'country': '美国 英国', 'movie_types': '剧情 科幻 冒险', 'score': '9.4', 'count': '1781396人评价', 'word': '爱是一种力量,让我们超越时空感知它的存在。'}, {'name': '楚门的世界', 'director': '彼得·威尔', 'time': '1998', 'country': '美国', 'movie_types': '剧情 科幻', 'score': '9.4', 'count': '1653321人评价', 'word': '如果再也不能见到你,祝你早安,午安,晚安。'}, {'name': '忠犬八公的故事', 'director': '莱塞·霍尔斯道姆', 'time': '2009', 'country': '美国 英国', 'movie_types': '剧情', 'score': '9.4', 'count': '1370045人评价', 'word': '永远都不能忘记你所爱的人。'}, {'name': '海上钢琴师', 'director': '朱塞佩·托纳多雷', 'time': '1998', 'country': '意大利', 'movie_types': '剧情 音乐', 'score': '9.3', 'count': '1643223人评价', 'word': '每个人都要走一条自己坚定了的路,就算是粉身碎骨。'}, {'name': '三傻大闹宝莱坞', 'director': '拉库马·希拉尼', 'time': '2009', 'country': '印度', 'movie_types': '剧情 喜剧 爱情 歌舞', 'score': '9.2', 'count': '1821545人评价', 'word': '英俊版憨豆,高情商版谢耳朵。'}, {'name': '放牛班的春天', 'director': '克里斯托夫·巴拉蒂', 'time': '2004', 'country': '法国 瑞士', 'movie_types': '剧情 音乐', 'score': '9.3', 'count': '1284102人评价', 'word': '天籁一般的童声,是最接近上帝的存在。'}, {'name': '机器人总动员', 'director': '安德鲁·斯坦顿', 'time': '2008', 'country': '美国', 'movie_types': '科幻 动画 冒险', 'score': '9.3', 'count': '1289003人评价', 'word': '小瓦力,大人生。'}, {'name': '无间道', 'director': '刘伟强', 'time': '2002', 'country': '中国香港', 'movie_types': '剧情 犯罪 惊悚', 'score': '9.3', 'count': '1328274人评价', 'word': '香港电影史上永不过时的杰作。'}, {'name': '疯狂动物城', 'director': '拜伦·霍华德', 'time': '2016', 'country': '美国', 'movie_types': '喜剧 动画 冒险', 'score': '9.2', 'count': '1879892人评价', 'word': '迪士尼给我们营造的乌托邦就是这样,永远善良勇敢,永远出乎意料。'}, {'name': '控方证人', 'director': '比利·怀尔德', 'time': '1957', 'country': '美国', 'movie_types': '剧情 犯罪 悬疑', 'score': '9.6', 'count': '533597人评价', 'word': '比利·怀德满分作品。'}, {'name': '大话西游之大圣娶亲', 'director': '刘镇伟', 'time': '1995', 'country': '中国香港 中国大陆', 'movie_types': '喜剧 爱情 奇幻 古装', 'score': '9.2', 'count': '1500049人评价', 'word': '一生所爱。'}, {'name': '熔炉', 'director': '黄东赫', 'time': '2011', 'country': '韩国', 'movie_types': '剧情', 'score': '9.4', 'count': '912917人评价', 'word': '我们一路奋战不是为了改变世界,而是为了不让世界改变我们。'}, {'name': '教父', 'director': '弗朗西斯·福特', 'time': '1972', 'country': '美国', 'movie_types': '剧情 犯罪', 'score': '9.3', 'count': '942640人评价', 'word': '千万不要记恨你的对手,这样会让你失去理智。'}, {'name': '当幸福来敲门', 'director': '加布里尔·穆奇诺', 'time': '2006', 'country': '美国', 'movie_types': '剧情 传记 家庭', 'score': '9.2', 'count': '1488138人评价', 'word': '平民励志片。'}, {'name': '触不可及', 'director': '奥利维·那卡什', 'time': '2011', 'country': '法国', 'movie_types': '剧情 喜剧', 'score': '9.3', 'count': '1083270人评价', 'word': '满满温情的高雅喜剧。'}, {'name': '怦然心动', 'director': '罗伯·莱纳', 'time': '2010', 'country': '美国', 'movie_types': '剧情 喜剧 爱情', 'score': '9.1', 'count': '1793004人评价', 'word': '真正的幸福是来自内心深处。'}]