正则表达式下+csv模块的使用
1. re模块方法补充
1.1 compile()方法
这个是re模块的工厂方法,用于将字符串形式的正则表达式编译为Pattern模式对象,可以实现更加效率的匹配。第二个参数flag是匹配模式,使用compile()完成一次转换后,再次使用还匹配模式的时候就不能进行转换了。经过compile()转换的正则表达式对象也能使用普通的re方法。
import re
# compile()方法传递匹配模板,返回Pattern对象
pat = re.compile(r'abc')
print(pat, type(pat))
result = pat.match(r'abc123').group()
print(result)
1.2 search()和findall()方法
search()方法:在文本内查找,返回第一个匹配到的字符串。它的返回值类型和使用方法与match()一样的,唯一的区别是查找的位置不用固定在文本的开头。
findall()方法:它与search、match的区别在于后两者都是单次匹配,找到一个就结束,直接返回不再查找。而fandall是全文查找,它的返回值是一个匹配到的字符串列表。这个列表没有group()方法,没有start、end和span,也不是匹配对象,仅仅是个列表。如果一项都没有匹配到就返回一个空列表。
1.3 split()方法
re模块的split()方法和字符串的split()方法很相似,都是利用特定的字符去分割字符串。但是re模块的split()可以使用正则表达式,相比更为灵活。
split()有个参数maxsplit,用于指定分割的次数。
[]里面的内容加了\对+、*、/进行转义。第三行命令中指定maxsplit=2,截取两段后,剩下的内容自动变成一个字符串。
1.4 sub()方法
sub()方法类似字符串的replace()方法,用指定的内容替换匹配到的字符,可以指定替换次数。
2. re模块分组
re模块里面有一个分组功能,所谓分组就是已经在匹配到的内容里面去筛选,相当于二次过滤。实现分组靠圆括号(),而获取分组的内容是group()、groups(),在之前已经介绍过。re模块里的几个重要方法在分组上有不同的表现形式,需要区别对待。
示例代码
import re
s = 'apple price is $66, banana price is $22'
# 需求:$66、$22
result = re.search(r'.+\$\d+.+\$\d+', s)
print(result.group())
result = re.search(r'.+(\$\d+).+(\$\d+)', s)
print('result.group() = ', result.group())
print('result.group(1) = ', result.group(1))
print('result.group(2) = ', result.group(2))
print('result.group(0) = ',result.group(0))
print('result.groups() = ', result.groups())
运行结果:
小总结:
group()/group(0) 返回整个分组;
group(1)/(2) 返回第一/二个分组的内容;
groups() 以元祖的形式返回所有的分组。
3. 案例分析
3.1 代码需求:爬取百度图片
百度图片是典型的瀑布流数据,岑参不齐的图片形式,在尾部不断地往下加载,对于用户操作简单。但是不便于python爬取,最好能想办法转化为传统的页面布局,分页式排版。
在之前有一种常见的方法是将上述url中的index改为flip,但现在百度网站的反爬手段提升,这个方法现在无法使用了。按照更为传统的方法,按快捷键F12,在network里寻找图片的url,如下图:
复制图片的url,并在网页源代码中查找该url,能找到对应的图片:
上图显示有三种url,分别为thumbURL、middleURL和hoverURL,笔者猜想应该是对应同一张图片中不同的尺寸或者分辨率。这里笔者以middleURL为例,简单地介绍爬取图片的思路以及相关代码的编写。
3.2 代码思路及编写
- 拿到目标url,这里指百度图片搜索后的首页;
import requests
import re
source_url = 'https://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm' \
'=result&fr=&sf=1&fmq=1608640730142_R&pv=&ic=&nc=1&z=&hd=&latest=©right=&se=1&showtab=0' \
'&fb=0&width=&height=&face=0&istype=2&ie=utf-8&sid=&word=%E5%A4%A9%E5%91%BD%E5%A5%87%E5%BE' \
'%A1'
request_headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 ',
'Referer': 'https://image.baidu.com/',
'Cookie':'BAIDUID=3DF52D16EFD175773213166BEE350E5A:FG=1;PSTM=1603507809;BIDUPSID=805830771AB53DC6285322A895A5C6F7;BDUSS=NQQ1VlRUUwYTVNSkh6QTNObXo4ZmJnVHMxbTduS0F3ZEhTWGZJU1NsY1VVYnRmSVFBQUFBJCQAAAAAAAAAAAEAAAAdAQ430MezvbH5vMUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABTEk18UxJNfWl;BDUSS_BFESS=NQQ1VlRUUwYTVNSkh6QTNObXo4ZmJnVHMxbTduS0F3ZEhTWGZJU1NsY1VVYnRmSVFBQUFBJCQAAAAAAAAAAAEAAAAdAQ430MezvbH5vMUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABTEk18UxJNfWl;BDORZ=B490B5EBF6F3CD402E515D22BCDA1598;BAIDUID_BFESS=DAC46E01AE25535771A3DA66E4CA85B7:FG=1;H_PS_PSSID=1449_33242_33306_33261_33236_31253_33285_33343_33313_33312_33311_33310_32446_33218_33309_33308_33307_22158;BDRCVFR[feWj1Vr5u3D]=I67x6TjHwwYf0;delPer=0;PSINO=5;BA_HECTOR=2024al208k2hal8gin1fu3q6a0r;BDRCVFR[dG2JNJb_ajR]=mk3SLVN4HKm;ab_sr=1.0.0_YzQ2ZjEwODI4ZTQ1MjE2MThjNzg4OGM3NWQ0ODhjYjhjNzQxMDE2NTUwZjJkOTQxZDBkMWNjMjY5ODM1N2VhOGMyMDU2MGM2MDZlYWU3MWE3MmU5ZDc2NmU0ZmUxNTZmYjJlYTEzZTY3NjYyOTgwMWQ1ZTQ3MDJmMTVhNWQ4Zjc='
}
cookie 和 user-agent根据自己机器的实际情况来写。
- 获取网页源码,向第一步的url发起请求后,获取网页的原始数据;
req_obj = requests.get(source_url, headers=request_headers)
req_cont = req_obj.content.decode('utf-8')
- 获取图片的url地址,用findall匹配需要的url
# "middleURL":"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=476331077,327453246&fm=26&gp=0.jpg"
result_url = re.findall(r'"middleURL":"(.*?")', req_cont) # 匹配到以后再分组,将结果保存到一个列表
print(result_url)
此处先运行现有的代码,查看是否匹配到需要的图片url地址,下图为运行结果:
笔者试了几个地址,大多都是有效的。
- 保存图片
count = 1 # 以图片顺序命名
for pict_url in result_url:
pict_path = 'pict/' + str(count) + '.jpg'
with open(pict_path, 'wb') as fobj:
# 可能出现网络问题
try:
pict_cont = requests.get(pict_url).content
fobj.write(pict_cont)
except Exception as e:
print(e, type(e))
count += 1
在指定的目录下,保存的图片如下:
4. csv模块的使用
4.1 基本概念
CSV(Common Separated Values),是一种常见的文本格式,用以存储表格数据,包括数字或者字符。很多程序在处理数据时都会碰到csv这种格式的文件。python自带了csv模块,专门用于处理csv文件的读取。
4.2 写入csv文件
- 通过创建writer对象,主要用到2个方法。一个是writerow(),写入一行。另一个是writerows()写入多行;
- 使用DictWriter()可以使用字典方式把数据写入进去。
writerow()/writerows()的使用:
import csv
# 定义表头
titles = ['name', 'age', 'kongfu']
conts = [['杨过', '36', '玄铁剑法'],
['郭靖', '30', '降龙十八掌'],
['张无忌', '24', '乾坤大挪移']]
with open('test.csv', 'w', encoding='utf-8', newline='') as fobj:
# 创建writer对象
write_obj = csv.writer(fobj)
# 写入表头
write_obj.writerow(titles)
# 写入具体内容
for cont in conts:
write_obj.writerow(cont)
运行结果,test.csv的内容如下:
name,age,kongfu
杨过,36,玄铁剑法
郭靖,30,降龙十八掌
张无忌,24,乾坤大挪移
上述代码通过for循环,一行一行写入内容,也可以通过writerows()一次写入多行:
# 写入具体内容
write_obj.writerows(conts)
运行结果和之前的一样。
DictWriter()的使用:
import csv
# 定义表头
titles = ['name', 'age', 'kongfu']
conts = [{'name': '杨过', 'age': '36', 'kongfu': '玄铁剑法'},
{'name': '郭靖', 'age': '30', 'kongfu': '降龙十八掌'},
{'name': '张无忌', 'age': '24', 'kongfu': '乾坤大挪移'}]
with open('test1.csv', 'w', encoding='utf-8', newline='') as fobj:
# 创建writer对象, 第二个参数传递表头,对应数据的key
writer_obj = csv.DictWriter(fobj, titles)
# 写入表头
writer_obj.writeheader()
# 写入内容
writer_obj.writerows(conts)
运行结果,test1.csv的内容如下:
name,age,kongfu
杨过,36,玄铁剑法
郭靖,30,降龙十八掌
张无忌,24,乾坤大挪移
注意:使用DictWriter()创建writer对象传递的表头会对写入数据的key进行检查,如不一致会报错。
4.3 读取csv文件
- 通过reader()读取到的每一条数据是一个列表。可以通过下标的方式获取具体某一个值;
- 使用DictReader()读取到的数据是一个字典,可以通过key值的方式获取数据。
reader()的使用:
import csv
with open('test1.csv', 'r', encoding='utf-8') as fobj:
reader_obj = csv.reader(fobj)
for cont in reader_obj:
print(cont)
运行结果:
DictReader()的使用:
import csv
with open('test1.csv', 'r', encoding='utf-8') as fobj:
reader_obj = csv.DictReader(fobj)
for cont in reader_obj:
print(cont)
print('name = ', cont['name'])
运行结果: