一、综述
- 为什么要学正则:处理数据(按照我们想要的方式存储和使用)
- 正则匹配规则:因为太长了,所以见本文最后
- match方法:从起始位置开始查找,一次匹配,返回object对象,使用group()获取对象中存储的匹配值
- search方法:从任何位置开始查找,一次匹配,返回object对象,使用group()获取对象中存储的匹配值
- findall方法:全部匹配,返回列表,若匹配表达式,要求同时还有其他匹配,则返回元组列表,
r'<img src2="(.*?)" alt="(.*?)"'
- finditer方法:全部匹配,返回迭代器,迭代器每次迭代,都会返回object对象,使用group()获取对象中存储的匹配值
- split方法:分割字符串,返回列表
- sub方法:替换
- 匹配中文:
re.compile(u'[\u4e00-\u9fa5]+')
- 贪婪模式与非贪婪模式
二、如何使用
用法:
- 根据匹配规则定义pattern : pattern=re.compile(r’匹配规则’) 或者直接 pattern = r’匹配规则’
- 使用re,根据匹配规则在对应的字符串中寻找符合匹配的:result=re.match(pattern,‘待匹配字符串’)
- 若返回对象,则用group获取;若返回列表,可直接读取;若返回迭代器,用循环获取,再用group
三、方法介绍
import re
s1 = 'hello world python high salary 123 456 Hello 789'
s2 = '235 hello world python high salary 123 456 Hello 789'
pattern = re.compile(r'\d+')
# match
print('*'*25,'match','*'*25)
result1 = re.match(pattern,s1)
result2 = re.match(pattern,s2)
print(result1)
print(result2)
print(result2.group())
# search
print('*'*25,'search','*'*25)
result3 = re.search(pattern,s1)
print(result3)
print(result3.group())
# findall
print('*'*25,'findall','*'*25)
pattern1 = re.compile(r'\w+')
result4 = re.findall(pattern1, s1)
print(result4)
# finditer
print('*'*25,'finditer','*'*25)
result5 = re.finditer(pattern, s1) # 返回可迭代对象,迭代器
for r in result5:
print(r)
print(r.group())
# split
print('*'*25,'split','*'*25)
s3 = '235 hello world python high salary 123 456 Hello 789'\
'this is the second line'\
'this is the third line'
pattern2 = re.compile(r'[\s.,\n]')
result6 = re.split(pattern2, s3)
print(result6)
# sub
print('*'*25,'sub','*'*25)
s4 = 'Hello World,Hello Python'
pattern3 = r'(\w+) (\w+)'
result7 = re.sub(pattern3, r'\2 \1', s4)
print(result7)
def convert(m):
return m.group(2).lower() + ' '+ m.group(1).upper()
result8 = re.sub(pattern3, convert, s4)
print(result8)
结果:
上述一堆方法中,最常用的还是findall
方法,所以熟悉findall
方法很重要
四、对findall方法的详细说明
import re
url= '<img src2="http://pic.sc.chinaz.com/Files/pic/pic9/201911/zzpic20859_s.jpg" alt="秘密图片">'
pattern = r'<img src2="(.*?)"'
print(re.findall(pattern,url))
print(re.match(pattern,url).group())
上述代码,使用的是同样的pattern,但是对于findall和match返回的是不一样的:
可以看到findall直接就返回分组的内容,而match这类返回object对象的,返回的是完整的匹配项
import re
td = '<td class>192.168.1.1</td>'
pattern1 = r'<td .*?>([\d\.]*)</td>'
pattern2 = r'<td (.*?)>([\d\.]*)</td>'
print(re.findall(pattern1,td))
print(re.findall(pattern2,td))
对于上述代码,我们可以看到,虽然都是findall,但是当class匹配项,不在分组内的时候,即使匹配也是不返回的。
import re
td = '<td class>192.168.1.1</td>'
pattern1 = r'<td .*?>[\d\.]*</td>'
print(re.findall(pattern1,td))
可以看到,上述代码,当没有分组出现的时候,返回的也是完整的匹配项
综上,使用findall结合分组的方法,可以很方便的获取一些src路径,这样就省去了将完整匹配项分割来取src的麻烦
即:对于定义了分组的,即匹配内容有()的
- 返回列表的,仅仅返回的分组内匹配内容
- 返回对象的,返回的全部匹配内容(包括分组内,分组外)
如:
<img src2="http://pic.sc.chinaz.com/Files/pic/pic9/201911/zzpic20859_s.jpg" alt="秘密照片">
pattern = r'<img src2="(.*?)"'
finditer,match,search 返回 <img src2="http://pic.sc.chinaz.com/Files/pic/pic9/201911/zzpic20859_s.jpg"
findall 返回 http://pic.sc.chinaz.com/Files/pic/pic9/201911/zzpic20859_s.jpg
五、实战爬取网页的时候,一个注意事项
re.S的使用,忽略换行符的影响
re.S是正则表达式中匹配的一个参数,如果没有re.S,则是只匹配一行有没有符合规则的字符串,如果没有则下一行重新匹配。
如果加上了re.S,则是将所有的字符串按一个整体进行匹配,findall将所有匹配到的结果封装到一个list中
re.findall(str_pattern, str, re.S)
六、正则匹配规则
七、实战:批量爬取图片
import requests
import re
import time
import random
# 爬取站长素材网站图片
url1 = 'http://sc.chinaz.com/tupian/xingganmeinvtupian.html'
url2 = 'http://sc.chinaz.com/tupian/xingganmeinvtupian_%d.html'
def download_imgs(result):
for url, name in result:
response = requests.get(url=url)
with open('./picture_download/%s.jpg' % name, mode='wb') as fp:
fp.write(response.content)
time.sleep(random.randint(1, 3)) # 随机休眠1到3秒
if __name__ == '__main__':
pic_url = []
for index in range(1, 2):
if index == 1:
url_tmp = url1
else:
url_tmp = url2 % index
response = requests.get(url_tmp)
response.encoding = response.apparent_encoding
pattern = r'<img src2="(.*?)" alt="(.*?)"'
result = re.findall(pattern,response.text)
download_imgs(result)
print('ok')
# with open('./picture.html', mode='w', encoding='utf-8') as fp:
# fp.write(response.text)
# print('已写入')
上述代码会爬取网络上的图片,出彩的亮点是findall一次匹配两个分组,这样返回的就是一个元组,即result是一个元组列表,列表中的每一个元组的第一项是正则中的第一个分组,第二项是正则中的第二个分组
另外一个亮点是,爬虫中经常用到的手段,随机休眠:time.sleep(random.randint(1, 3)) # 随机休眠1到3秒
可以防止被封IP