[Pt_09] Python数据清洗之正则表达式应用
目录结构
一、Python正则表达式
1. 匹配普通字符
2. 匹配通用字符
3. 匹配数字、中文、英文(通用字符)
4. 原子表
5. 常用元字符
6. 匹配字符出现的次数
7. 匹配多个正则表达式
8. 分组
9. 贪婪模式 & 非贪婪模式
10. compile函数
11. match函数、search函数
12. findall()函数、finditer()函数
13. split()函数、sub()函数
二、Python爬虫实践
1. 爬取网站电话号码
2. 爬取电影排名列表
<关联1> JavaScript正则表达式
<关联2> Shell编程-正则 & 文本字符处理命令
一、Python正则表达式
原子:正则表达式中实现匹配的基本单位(被匹配的对象)
元字符:正则表达式中具有特殊含义的字符
1. 匹配普通字符
以普通字符作为原子(匹配一个普通字符)
search函数:搜寻字符串中是否包含所匹配的字符
# 从st字符串中搜寻符合pat正则匹配规则的字符
re.search(pat,st)
normalMatch.py
import re
st="春天夏天秋天冬天"
pat="夏天"
result=re.search(pat,st)
print(result)
![](https://img.haomeiwen.com/i4866277/63dafa69e6a9c9cb.png)
2. 匹配通用字符
常见通用字符:
字符 | 等价类 | 含义 |
---|---|---|
\d | [0-9] | 数字字符 |
\D | [^0-9] | 非数字字符 |
\s | [\t\n\x0B\f\r] | 空白符 |
\S | [^\t\n\x0B\f\r] | 非空白符 |
\w | [a-zA-Z_0-9] | 任意单词字符 (字母、数字、下划线) |
\W | [^a-zA-Z_0-9] | 非单词字符 |
结合英文原意记忆:
d ==> digit(数字)
s ==> space(空白)
w ==> word(单词)
generalMatch.py
import re
st="13800138000@@***.@163.com"
pat1=r"\d\d\d" # r表示取消转义
pat2=r"\d\d\D"
pat3=r"\w\w\w"
pat4=r"\w\W\W"
i=1
for pat in pat1,pat2,pat3,pat4:
result=re.search(pat,st)
print("result"+str(i)+":",result)
i=i+1
![](https://img.haomeiwen.com/i4866277/ff67bc8c6c65e757.png)
3. 匹配数字、中文、英文(通用字符)
数字:[0-9]
英文:[a-zA-Z]
中文:[\u4e00-\u9fa5]
generalMatch2.py
import re
st="asd2019f0@Python@***正则.@.com"
pat1=r"[0-9]{4}"
pat2=r"P[a-zA-Z]{5}"
pat3=r"[\u4e00-\u9fa5]{2}"
i=1
for pat in pat1,pat2,pat3:
result=re.search(pat,st)
print("result"+str(i)+":",result)
i=i+1
![](https://img.haomeiwen.com/i4866277/178c14aafce5bc9f.png)
4. 原子表
定义一组平等的原子,只要有一个满足条件,则表示匹配成功
phone.py
import re
st="13800138000"
pat=r"^1[35789]\d{9}$"
result=re.search(pat,st)
print(result)
![](https://img.haomeiwen.com/i4866277/258dc5b30713a7f5.png)
5. 常用元字符
正则中具有特殊含义的字符
符号 | 含义 |
---|---|
. | 匹配任意字符(换行符"\n"除外) |
^ | 匹配字符串的开始位置 |
$ | 匹配字符串的结束位置 |
* | 重复任意次(≥0次)前面的原子 |
? | 含义①:重复0或1次前面的原子 含义②:非贪婪匹配。应用:aaa(.*?)bbb |
+ | 重复1次及以上(≥1次)前面的原子 |
6. 匹配字符出现的次数
匹配一个出现n次的字符串,n=0,1,...,N
字符 | 含义 |
---|---|
? | 0或1 次 |
+ | ≥1 次 |
* | ≥0 次(任意次) |
{n} | n 次 |
{n,m} | n到m 次 |
{n,} | ≥n 次 |
7. 匹配多个正则表达式
多个正则之间用符号 |
连接,任意一个匹配成功则表示匹配ok
multiMatch.py
import re
st1="asd2019f0"
st2="@Python@*"
st3="**正则.@.com"
pat=r"\d{4}|P[a-zA-Z]{5}|[\u4e00-\u9fa5]{2}"
ls=[]
for st in st1,st2,st3:
result=re.search(pat,st)
print(result)
![](https://img.haomeiwen.com/i4866277/f0b4f55340fb66f1.png)
8. 分组
分组:使用符号()
输出分组内容:group(num)、groups();num表示第几个分组对应的数字
group.py
import re
st="asd2019f0@Python@***正则.@.com"
pat=r"(\d{4}).*(P[a-zA-Z]{5}).*([\u4e00-\u9fa5]{2})"
result=re.search(pat,st)
result1=result.group(1)
result2=result.group(1,2,3)
result3=result.groups()
for result in result1,result2,result3:
print(result)
![](https://img.haomeiwen.com/i4866277/a130ecbf8f98ba33.png)
9. 贪婪模式 & 非贪婪模式
贪婪模式:尽可能多的匹配。Python默认贪婪模式
非贪婪模式:尽可能少的匹配(?
)
greedyModel.py
import re
st="<div>AAA</div>正则<div>BBB</div>"
pat1=r"<div>.*</div>" # 贪婪模式
pat2=r"<div>(.*?)</div>" # 非贪婪模式
for pat in pat1,pat2:
result=re.search(pat,st)
print(result)
![](https://img.haomeiwen.com/i4866277/a0b75975acd0e97c.png)
10. compile函数
将正则表达式转换成内部格式,以提高正则执行效率,适用于匹配大量次数的场景
compile1.py
import re
st="111Python@@@123"
# 方式1:
# pat=r"\d+"
# result=re.search(pat,st)
# 方式2:
pat=re.compile(r"\d+")
result=pat.search(st)
print(result)
![](https://img.haomeiwen.com/i4866277/1b01e15bdf52837e.png)
compile2.py
import re
st="111PyThon@@@123"
pat1=re.compile(r"python") # 默认大小写严格区分
pat2=re.compile(r"python",re.I) # 模式修正符:忽略大小写
result1=pat1.search(st)
result2=pat2.search(st)
print(result1)
print(result2)
![](https://img.haomeiwen.com/i4866277/a559f630518bd73b.png)
11. match函数、search函数
match函数:匹配开头位置
search函数:匹配任意位置
PS:以上2个函数都是一次匹配,匹配到一次后就不再往后继续匹配
match-search.py
import re
st1="111python@@@123"
st2="python@@@123"
pat=re.compile(r"python")
result1=pat.match(st1)
result2=pat.match(st2)
result3=pat.search(st1)
result4=pat.search(st2)
result5=pat.match(st2).group()
result6=pat.search(st2).group()
for result in result1,result2,result3,result4,result5,result6:
print(result)
![](https://img.haomeiwen.com/i4866277/5528b7996210bb31.png)
12. findall()函数、finditer()函数
findall()函数:查找所有匹配的内容,存放到列表中
finditer()函数:查找所有匹配的内容,存放到迭代器中
findall-finditer.py
import re
st="hi-------hi-----\
-------hi------\
hi-----hi2019hi----"
pat=re.compile(r"hi")
result1=pat.findall(st)
print(result1,"\n")
result2=pat.finditer(st)
print(result2,"\n")
ls=[]
for i in result2:
ls.append(i.group())
print(ls)
![](https://img.haomeiwen.com/i4866277/17dd6dfe57d5d21e.png)
13. split()函数、sub()函数
split()函数:将匹配的子串作为分隔符,对字符串进行分割,并返回列表
sub()函数:将匹配的子串替换为指定子串
split-sub.py
import re
st="AAA。。。BBB。。。。。。CCC...111---EEE"
pat1=re.compile(r"[。\.-]+")
result1=pat1.split(st)
print(result1,"\n")
pat2=re.compile(r"\d+")
result2=pat2.sub("DDD",st)
print(result2,"\n")
result3=pat1.split(result2)
print(result3)
![](https://img.haomeiwen.com/i4866277/de012e069507e3d3.png)
二、Python爬虫实践
1. 爬取网站电话号码
目标:在某公开电话网站,利用爬虫获取常用电话的名称、电话号码信息
分析:
通过简单方式获取响应数据,分析其中的html响应数据结构,构造匹配所需内容的正则表达式
getPhoneNumber.py
import requests
import re
header={
"user-agent":"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
}
url="https://changyongdianhuahaoma.51240.com/"
resp=requests.get(url,headers=header).text
print(resp)
![](https://img.haomeiwen.com/i4866277/28875809f272544d.png)
构造正则匹配规则:
名称:
<tr bgcolor="#EFF7F0">[\s\S]*?<td>(.*?)</td>[\s\S]*?<td>[\s\S]*?</td>[\s\S]*?</tr>
电话:
<tr bgcolor="#EFF7F0">[\s\S]*?<td>[\s\S]*?</td>[\s\S]*?<td>(.*?)</td>[\s\S]*?</tr>
代码实现:
import requests
import re
header={
"user-agent":"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
}
# 发送GET请求,获取响应数据
# 若获取不到响应数据,根据需要关闭SSL验证
url="https://changyongdianhuahaoma.51240.com/"
resp=requests.get(url,headers=header,verify=False).text
# 正则规则
pat1=r'<tr bgcolor="#EFF7F0">[\s\S]*?<td>(.*?)</td>[\s\S]*?<td>[\s\S]*?</td>[\s\S]*?</tr>' # 匹配电话名称
pat2=r'<tr bgcolor="#EFF7F0">[\s\S]*?<td>[\s\S]*?</td>[\s\S]*?<td>(.*?)</td>[\s\S]*?</tr>' # 匹配电话号码
# 转换正则为内部格式
pattern1=re.compile(pat1)
pattern2=re.compile(pat2)
# 从响应数据中匹配数据(匹配结果为列表类型)
data1=pattern1.findall(resp)
data2=pattern2.findall(resp)
# 创建列表,存储"电话名称:电话号码"
result=[]
# 遍历每个电话
for i in range(0,len(data1)):
result.append(data1[i]+": "+data2[i]) # 构造每一个电话号码
# 打开指定文本文件,以追加方式写入数据
f=open(r"D:\CI_Env\Python_Test\file\003.txt","a")
f.write(result[i]+"\n")
f.close()
执行结果:
![](https://img.haomeiwen.com/i4866277/442f09e1ee598f06.png)
2. 爬取电影排名列表
目标:访问某电影网站某类型电影排行榜,爬取对应电影名称、评分、排名数据
分析:
定位到排行榜页码,向下滑动页面的过程中,排行榜的电影每次以20个片名累积加载,采用JS异步请求(AJAX请求)方式加载数据,对应的数据请求url如下:
https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=0&limit=20
https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=20&limit=20
https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=40&limit=20
![](https://img.haomeiwen.com/i4866277/513568507bd47800.png)
代码实现:
movieRank.py
import urllib.request
import re
header={
"user-agent":"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
}
# 排名前20的请求链接
url="https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=0&limit=20"
# 创建请求对象
req=urllib.request.Request(url,headers=header)
# 发送请求,获取响应
data=urllib.request.urlopen(req).read().decode()
# 定义正则:匹配电影名称/评分/排名
pat1=r'"title":"(.*?)"'
pat2=r'"rating":\["(.*?)","\d+"\]'
pat3=r'"rank":(\d+)'
# 转换正则为内部格式
pattern1=re.compile(pat1)
pattern2=re.compile(pat2)
pattern3=re.compile(pat3)
# 从响应数据中匹配数据(匹配结果为列表类型)
data1=pattern1.findall(data)
data2=pattern2.findall(data)
data3=pattern3.findall(data)
for i in range(len(data1)):
print("排名:",data3[i]+"\t\t"+"电影名:"+data1[i]+"\t\t"+"评分:"+data2[i])
执行结果:
![](https://img.haomeiwen.com/i4866277/7e586cab8400d94e.png)