打开百度贴吧,找到图片吧,找到每日一图,有577张图片。
在网页中图片是以二进制的形式存在的,我们要先拿到图片的url地址,去发起请求,以二进制保存到本地。
光标在任意一张图片处,点右键,检查,光标会定位到图片所在的位置,复制里面的url,去浏览器中可以打开这张图片,但是图片的url值能并非在网页源码中,直接向这个url发起请求并不能得到想要的数据,这时候就需要分析数据接口,去查找数据
点击右键,检查,点击XHR,找到左侧 “list?kw=”的文件,依次点开preview --> data --> pic_list,可以看到下面有一系列的标签内容。
点开其中一个标签,可以看到有两个url地址,复制在网页中打开,第一个地址显示的是像素比较小的图片,第二个地址显示的图片像素相对较高。
在headers里找到url,复制到浏览器中打开,出现的格式很乱,可以装一下 “JSONView-for-Chrome-master”,辅助查看代码。
安装步骤:浏览器右上角三个点,扩展,把"JSONView-for-Chrome-master"文件夹下的"WebContent"文件拖到浏览器中,重启浏览器就可以了。
这时,再打开刚复制的url,就会以清晰的网页结构形式显示出网页代码,格式比较清晰,方便做数据分析。可以看到整个代码是字典结构。
我们可以看到需要的图片url地址,所以需要向headers里面的url发起请求就可以获得图片的信息。
总结:
通过分析 我们发现源码中并没有我们想要的数据,通过network 找到了真正的数据接口,要对字典格式的数据进行提取
访问的url:https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E5%9B%BE%E7%89%87&alt=jview&rn=200&tid=1433290932&pn=1&ps=1&pe=40&info=1&_=1629980362729
import requests
import pprint
import json
import re
url = 'https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E5%9B%BE%E7%89%87&alt=jview&rn=200&tid=1433290932&pn=1&ps=1&pe=40&info=1&_=1629980362729'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36 Edg/92.0.902.62'
}
res = requests.get(url, headers=headers)
"""得到json数据一般有两种方式"""
# 第一种,用requests模块提供的得到json数据的方式
# print(res.json()) # 页面分析的时候网页源码是字典格式的,用json打印字典格式
# pprint.pprint(res.json()) # 打印json格式,层级关系很清晰的字典格式
# pprint.pprint(res.json()['data']['pic_list']) #字典格式从key值为data开始,下一级key值为pic_list,逐级提取value值
# 得到是列表,每一个图片都以字典的格式存放,遍历取出每一个图片
# pic_list = res.json()['data']['pic_list']
# for pic in pic_list: # 遍历出每一个图片,利用key值找出单个图片的url
# print(pic['purl'])
# print('*'*50)
# 第二种方法,需要导入python内置的json模块 import json
# 传入的是字符串,把字符串转为字典格式
# text获取响应对象里面的文本内容,res.text得到的是字符串
# content直接抓取获得的内容,对内容不做任何处理,res.content.decode()
# html = json.loads(res.text)
# pprint.pprint(html) #字典格式从key值为data开始,下一级key值为pic_list
# 得到是列表,每一个图片都以字典的格式存放,遍历取出每一个图片
# pic_list = html['data']['pic_list']
# for pic in pic_list: # 遍历出每一个图片,利用key值找出单个图片的url
# print(pic['purl'])
# # print('*'*50)
"""用正则同样可以提取数据"""
html = res.text
# print(html)
# 相当于findall(返回的是列表)
# 把需要匹配的图片地址用(.*?)代替,
# ?P把标签重命名为img_url,match,find都可以用,但是findall不能用
result = re.finditer(r'"purl":"(?P.*?)"' , html, re.S)
# print(result)
"""以上的操作都是提取出图片的url值,下面的操作是对获取的url发起请求,把图片保存到本地"""
name = 1
for i in result:
# print(i.groupdict()) # key是重命名的名字,value是取得的图片地址
# print(i.groupdict()['img_url']) # 方式一:通过key值取出value
# print(i.group('img_url')) # 方式二:直接获取
img_down_url = i.group('img_url') # 图片的url
# 在当前文件夹下创建img文件夹用于存放图片,用+1的方式存储图片名字,在循环前定义保存图片的name = 1
filename = './img/{}.jpg'.format(name) # 定义文件名
# 向图片的url地址发起请求
respons = requests.get(img_down_url)
# wb以二进制的形式写入图片,视频,音频
print('正在写入第%d张图片' % name)
with open(filename,'wb') as f:
# 用content获取图片,视频,音频
f.write(respons.content)
name += 1
程序完成之后,发现贴吧里面有500多张图片,而只写入了40张,回到网页 --> 检查 --> Network --> Preview --> data --> pic_list 中可以看到,里面存在的数据就只有40张,此时鼠标放到网页滑轮向下滚动,会发现XHR 左侧"名称"下会逐渐的多出来 此时就要对url进行替换,这是name定义要放在这个循环上面,否则会被重复覆盖。 注意,下面的代码都是在for循环下的,都要统一的进行缩进,此时得到的结果只有200张图片,回到网页看一下,577张图片存放在3页,所以还要有一个翻页操作,仔细看一下后两页的url,跟第一页的几乎一样,不同点在于第一页pn=1,第二页pn=2,第三页pn=3,所以对pn进行一个循环就可以了实现翻页了。 这样的话就把网页中的577张图片都爬取下来了。
第一个url
https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E5%9B%BE%E7%89%87&alt=jview&rn=200&tid=1433290932&pn=1&ps=1&pe=40&info=1&_=1629980362729
第二个url
https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E5%9B%BE%E7%89%87&alt=jview&rn=200&tid=1433290932&pn=1&ps=41&pe=80&wall_type=h&_=1629988753220
第三个url
https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E5%9B%BE%E7%89%87&alt=jview&rn=200&tid=1433290932&pn=1&ps=81&pe=120&wall_type=h&_=1629988763909
第四个url
https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E5%9B%BE%E7%89%87&alt=jview&rn=200&tid=1433290932&pn=1&ps=121&pe=160&wall_type=h&_=1629988858216
第五个url
https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E5%9B%BE%E7%89%87&alt=jview&rn=200&tid=1433290932&pn=1&ps=161&pe=200&wall_type=h&_=1629988863546
可以看一下规律
pn ps pe
1 1 40
1 41 80
1 81 120
1 121 160
1 161 200
其中pn=1代表的是第一页,横向ps与pe相差39,纵向ps,pe相差40
for m in range(1, 162, 40):
# print(m) # 先获取到i的值,也就是ps的值
print(m, m+39) # 用ps的值加上39 得到pe的值
name = 1
for m in range(1, 162, 40):
time.sleep(3)
url = 'https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E5%9B%BE%E7%89%87&alt=jview&rn=200&tid=1433290932&pn=1&ps={}&pe={}&info=1&_=1629980362729'.format(m, m+39)
name = 1
for n in range(1, 4):
time.sleep(3)
for m in range(1, 162, 40):
time.sleep(3)
url = 'https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E5%9B%BE%E7%89%87&alt=jview&rn=200&tid=1433290932&pn={}&ps={}&pe={}&info=1&_=1629980362729'.format(n, m, m+39)