day2023-3-28-正则表达式

时间:2023-04-01 23:02:10

补充:os模块

import os

# os.mkdir(文件路径) —— 在指定的位置创建指定文件夹
# os.mkdir('files/test')

# os.path.exists(文件夹路径/文件路径) —— 判断指定文件夹或者文件是否存在
if not os.path.exists('files/test'):
    os.mkdir('files/test')

一、正则表达式

1.正则的作用

正则表达式是一种可以让复杂的字符串变得简单的工具;

写正则表达式就是用正则符号来描述字符串应该满足的规则。

import re
from re import *

# 案例1:判断一个字符串是否是手机号
tel = 15775898786
result = re.fullmatch(r'1[3-9]\d{9}', tel)
if result:
    print('合法')
else:
    print('不合法')

# 案例2:提取字符串中所有的数字子串,并且求和
str1 = 'jisunf78-=3.67会议激素19'
result = re.findall(r'\d+\.?\d*', str1)
print(sum([float(x) for x in result]))

二、正则语法

1.第一类符号:匹配类符号

1)普通符号 —— 在正则表达式中表示符号本身的符号

from re import fullmatch, findall, search
result = fullmatch(r'abc', 'abc')
print(result)

2). —— 匹配任意一个字符

result = fullmatch(r'.bc', ';bc')
print(result)

result = fullmatch(r'.bc.', '2bcd')
print(result)

3)\d —— 匹配任意一个数字字符

result = fullmatch(r'\d\dabc', '72abc')
print(result)

4)\s —— 匹配任意一个空白字符

空白字符:空格(’ ‘)、换行(’\n’)、水平制表符(‘\t’)

result = fullmatch(r'123\sabc', '123\nabc')
print(result)

result = fullmatch(r'\d\d\s\d', '89 1')
print(result)

5)\w —— 匹配任意一个字母、数字、下划线或者中文

result = fullmatch(r'abc\w123', 'abc_123')
print(result)

6)\D\S\W —— 分别和\d、\s、\w的功能相反

# \D —— 匹配任意一个非数字字符
result = fullmatch(r'abc\D123', 'abcj123')
print(result)
# \S —— 匹配任意一个非空白字符
result = fullmatch(r'abc\S123', 'abc-123')
print(result)
# \W —— 匹配任意一个 非 字母、数字、下划线或者中文
result = fullmatch(r'abc\W123', 'abc+123')
print(result)

7)[字符集] —— 匹配在字符集中的任意一个字符

"""
[abc] —— 匹配a,b,c
[abc\d] —— 匹配a,b,c,任意数字:[abc0123456789]
[1-5] —— 匹配字符1到字符5中的任意一个字符
[a-z] —— 匹配任意一个小写字母
[A-Z] —— 匹配任意一个大写字母
[a-zA-Z] —— 匹配任意一个字母
[a-zA-Z\d] —— 匹配任意一个字母或者数字
[a-z=%] —— 
[\u4e00-\u9fa5] —— 匹配任意一个中文
"""
result = fullmatch(r'abc[M9你\d]123', 'abc0123')
print(result)

result = fullmatch(r'abc[\u4e00-\u9fa5]123', 'abc胡123')
print(result)

8)[^字符串] —— 匹配不在字符集中的任意一个字符

result = fullmatch(r'abc[^MN]123', 'abcl123')
print(result)

result = fullmatch(r'abc[^a-z]123', 'abcl123')
print(result)      # None

2.第二类符号:匹配次数符号

匹配类符号匹配次数

1)* —— 任意次数(0次或者1次或者次数)

"""
a* ——  a出现任意多次
\d* —— 任意多个任意数字
[abc]* 
"""
result = fullmatch(r'1a*2', '1aaaaaaaaaaaa2')
print(result)

result = fullmatch(r'M\d*2', 'M123452')
print(result)

result = fullmatch(r'M[3-9]*2', 'M3452')
print(result)

2)+ —— 一次或者多次(至少一次)

result = fullmatch(r'1a+2', '12')
print(result)      # None

3)? —— 0次或者1次

result = fullmatch(r'1a?2', '1aa2')
print(result)      # None

4){}

"""
{N} —— N次
{M,N} —— M到N次
{M,} —— 至少M次
{,N} —— 最多N次
"""
result = fullmatch(r'1a{3}2', '1aa2')
print(result)     # None

result = fullmatch(r'1a{3,6}2', '1aaaa2')
print(result)


result = fullmatch(r'\d{3}', '123')
print(result)     # <re.Match object; span=(0, 3), match='123'>
# 练习:写一个正则表达式,可以匹配任意一个除了0的整数。
# 合法:233、+234、-7283、100、-2000
# 不合法:0、0002、2.23
result = fullmatch(r'[+-]?[1-9]\d*', '-1023')
print(result)

5)贪婪和非贪婪模式

在匹配次数不确定的时候,如果有多种次数都可以匹配成功,贪婪取最多的那个次数,非贪婪取最少的那个次数。
贪婪模式:+、?、、{M,N}、{M,}、{,N}
非贪婪模式:+?、??、
?、{M,N}?、{M,}?、{,N}?

# 'ahkmb','ahkmb首饰b','ahkmb首饰b奇数b'
result =search(r'a.+b', 'ahkmb首饰b奇数b')
print(result)      # <re.Match object; span=(0, 11), match='ahkmb首饰b奇数b'>

result =search(r'a.+?b', 'ahkmb首饰b奇数b')
print(result)      # <re.Match object; span=(0, 5), match='ahkmb'>

3.第三类符号:分组和分支

1)分组 —— ()

正则表达式中可以用()将部分内容括起来表示一个整体。括号括起来的部分就是一个分组。
a.整体操作的时候需要分组
b.重复匹配 —— 正则中可以通过\M来重复它前面第M个分组匹配的结果
c.捕获 —— 提取分组匹配到的结果(捕获分为自动捕获(findall)和手动捕获)

# '23M'、'89K23L'、'67C34D73Y29S',...
result = fullmatch(r'(\d\d[A-Z])+', '67C34D73Y29S')
print(result)

# '23M23','90K90','34f34'
result = fullmatch(r'(\d\d)[A-Z]\1', '23M23')
print(result)

result = fullmatch(r'(\d\d{2})([a-z]{4})=\2\1{2}', '233dddd=dddd233233')
print(result)

findall在正则表达式中有分组的时候,会自动提取正则匹配结果中分组匹配到的内容

message = '奇数偶数234jiushe897dhue-=2398'
result = findall(r'[\u4e00-\u9fa5](\d+)', message)
print(result)
# 提取身高
message = '我是小明,今年23岁,身高180厘米,体重70kg'
result = search(r'身高(\d+)厘米,体重(\d+)kg', message)

匹配对象.group(N) —— 获取匹配结果中指定分组匹配到的内容

print(result)      # <re.Match object; span=(11, 25), match='身高180厘米,体重70kg'>
print(result.group())       # 身高180厘米,体重70kg
print(result.group(1), result.group(2))      # 180 70

2)分支 —— |

正则1|正则2|正则3|… —— 先用正则1进行匹配,匹配成功直接成功;匹配失败用正则2进行匹配,…

result = fullmatch('\d{3}|[a-z]{2}', 'mn')
print(result)

# 'abc34', 'abcKJ', 'abc78', 'abcOP'
result = fullmatch(r'abc\d\d|abc[A-Z]{2}', 'abcJK')
print(result)

result = fullmatch(r'abc(\d\d|[A-Z]{2})', 'abcJK')
print(result)

4.转义符号

转义符号:在本身具有特殊功能或者特殊意义的符号前加 \ ,让特殊符号变成普通

# 匹配整数部分和小数部分都是两位数的小数
result = fullmatch(r'\d\d\.\d\d', '23.45')
print(result)

result = fullmatch(r'\d\+\d', '3+7')
print(result)

# '(amd)'
result = fullmatch(r'\([a-z]{3}\)', '(jsk)')
print(result)

注意:单独存在有特殊意义的符号,在[]中它的功能会自动消失

result = fullmatch(r'\d[+.?*()]\d', '3+4')
print(result)

三、re模块

re模块:提供python中所有的和正则相关的函数

from re import fullmatch, findall, search, match, finditer, split, sub
  1. fullmatch(正则, 字符串) —— 用整个字符串和正则,匹配成功返回匹配对象,匹配失败返回None
  2. findall(正则, 字符串) —— 获取字符串中所有满足正则的子串,默认返回一个列表,列表中的元素是所有匹配到的子串(存在自动捕获现象)
  3. search(正则, 字符串) —— 匹配第一个满足正则的子串,匹配成功返回匹配对象,匹配失败返回None
  4. split(正则, 字符串) —— 将字符串中所有满足正则的子串作为切割点进行切割
  5. split(正则, 字符串, N) —— 将字符串中前N个满足正则的子串作为切割点进行切割
  6. sub(正则, 字符串1, 字符串2) —— 将字符串2中所有满足正则的子串都替换成字符串1
  7. sub(正则, 字符串1, 字符串2, N)
  8. finditer(正则, 字符串) —— 获取字符串中所有满足正则的子串,返回一个迭代器,迭代器中的元素是匹配对象
  9. match(正则, 字符串) —— 匹配字符串开头

注意:python中表示一个正则表达式一般使用r字符串

str1 = '奇数偶数2亏待你489毒池234空'
print(split(r'\d+', str1))      # ['奇数偶数', '亏待你', '毒池', '空']
print(split(r'\d+', str1, 2))   # ['奇数偶数', '亏待你', '毒池234空']
print(sub(r'\d', '+', str1))    # 奇数偶数+亏待你+++毒池+++空

message = '妈的,sb,都打起来了你还在打野!草!'
print(sub(r'妈的|sb|草|操|艹|f\s*u\s*c\s*k', '*', message))    # *,*,都打起来了你还在打野!*!

str1 = '奇数偶数2亏待你489毒池234空'
result = finditer(r'\d+', str1)
print(list(result))         # [<re.Match object; span=(4, 5), match='2'>, <re.Match object; span=(8, 11), match='489'>, <re.Match object; span=(13, 16), match='234'>]
# 1)忽略大小写:(?i)
print(fullmatch(r'(?i)abc', 'abc'))
print(fullmatch(r'(?i)abc', 'Abc'))
print(fullmatch(r'(?i)abc', 'ABc'))
print(fullmatch(r'(?i)abc', 'aBc'))
# 2)单行匹配:(?s)
# 多行匹配(默认):. 不能和换行符进行匹配
# 单行匹配:. 可以和换行符进行匹配
print(fullmatch(r'abc.123', 'abc\n123'))    # None
print(fullmatch(r'(?s)abc.123', 'abc\n123'))      # <re.Match object; span=(0, 7), match='abc\n123'>

message = """
'name:"jsdk2-
jisok"'
"""
result = findall(r'(?s)name:"(.+)"', message)
print(result)      # ['jsdk2-\njisok']

练习

使用正则提取豆瓣top250每个电影的详情页地址

import requests
from re import findall

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',
    'cookie': 'BIDUPSID=A680D8CEFEAC65307965AEEFA7386849; PSTM=1677116640; BAIDUID=A680D8CEFEAC6530CC088914CFB657B3:FG=1; BD_UPN=12314753; BAIDUID_BFESS=A680D8CEFEAC6530CC088914CFB657B3:FG=1; ZFY=vaDaTi368yipgGZpVQL00KrnlEz88JWZ20zpNYGQVtI:C; COOKIE_SESSION=403843_1_4_7_3_6_1_0_3_5_2_0_403842_0_4_0_1679110087_1678182048_1679110083%7C7%230_1_1678182042%7C1; channel=chdb.s.3jdh.com; __bid_n=1870eeee282c21eba74207; FPTOKEN=4+MpAYwzZnRqgzjxYNT0wbLcN0FjxfEfFnUlkJWXqyZtczFiDKJ0FxsNExLcE9Cpyiw/SzLS9xWcLiCpFeXd5HZ+Sbta4AwRaWkwe2hvpdbS8HgcT67wnaS5uyUf0wyk3h5LtG5idaJxa1cnlcttmtX7qFsOS9iU/P3QpqP/haHj1bQvLTjpqvqhDHYHU6CRNZT63r0gAch/p61KQbClHaGTYdM+L4HcMxfj1AEgI4cz61fgJwFtuSY0ijK1lpO1nM5f6fLxn3tQz+1wfARxIQ86Vb43lw6yi3AX8sgxJLEIC+8+xQtH+3yZcBqQOMURZYcprAEQfdfCr+ytxMHyGoYGHHgatbhWOSbtqAIWKko45z6WDTCmChXZsYVp41BkSOIan5UbXCDwnO0jmVICBg==|kXj3FDr48e1/ID2yF1sMI09I3hp//yzUWBBKMcHnctk=|10|159a962b16701ba8d05dac30b2e0613c; BDRCVFR[PgFsD-oVCXs]=Sgq2UtKbV53nvGbpa4WUvY; delPer=0; BD_CK_SAM=1; PSINO=7; H_PS_PSSID=26350; BA_HECTOR=0h8h04ala005a08g802h24f21i22gla1n; BDORZ=FFFB88E999055A3F8A630C64834BD6D0; baikeVisitId=96d6d66c-40fa-4df5-9050-b5dd62676860; B64_BOT=1; ab_sr=1.0.1_NmRlMWExYTJmMDgxMTk3NmQ3MGMyMTA0N2I0M2ZkMmViZThhNWYzNGE2MmQ4NDYwYmY4YzE5YzYyMGZlZjJkMmZkY2Y3OWZlZGI3MjVlZmQxODY4ZWFiNTVkYTg1ZjExN2I0MmRlNGVmZWM0MDFlYzcyOGU3YWIyZTAzMmE2ZmZhNzdlNzczMDFlNGFkZjVhNTkxYjlkOWYxOTQ2ZjE1Nw==; H_PS_645EC=53dc0TItHpWat2m269u0gLvvlXcIkQKUs9AgCcryoRf2fMxOithlPnm1eJMXHPDZ5EuPZGND2L5b; BD_HOME=1'
}
response = requests.get('https://movie.douban.com/top250?start=0&filter=', headers=headers)

# 方法1
result = list(set(findall(r'https://movie.douban.com/subject/.+?/', response.text)))
print(result)

# 方法2
result = findall(r'<a href="(.+?)" class=""', response.text)
print(result)