爬百度图片和之前爬搜狗图片一样,都没啥难度,只是想学习巩固下response对象的各个属性。
-
text:
text其实不知道别人网站是用的什么编码格式,它解码的时候其实是“猜”的:
返回值是字符串类型,本质上就是content返回的字节的解码。
当然,大多数时候可以猜测正确,但是少数时候可能猜测错误,所以尽量少用text,多用content然后自己对字节文件解码。 或者你在调用text前手动设置解码格式:用response对象的encoding属性:
比如这里,我先查看要爬的网站:百度图片的meta标签里的charset属性:
现在我知道了网站是utf-8编码,然后我可以这样:
这样是没问题的:有正确的结果: -
content:
返回值是bytes类型,也就是字节流:
检查一下:%B9%D8%CF%FE%CD%AE这个一看就是urlencoded格式的数据,我们手动解码看下:
注意解码格式要选gbk,我也不知道为啥,可能百度网页源码是utf-8编码,域名解析后是gbk,算了不重要。
\xe5\x85\xb3\xe6\x99\x93\xe5\xbd\xa4这个就是bytes类型的数据了,我们解码下:
也是对的。
3.查看请求头:
这里可以很明显的看到如果你不加user-agent的话:python会自动帮你加一个带有python字样的user-agent:
4.请求的cookie:
这样查看请求的cookies,注意这里一定要加上代表私有属性,不加的话访问不到cookies属性: 结果:
因为我访问的时候确实没加cookies,也没去抓取,所以这是对的。
-
响应头:
响应头就是这个:
你在抓包的时候会经常看到。
程序结果:
响应头其实是很有用的,很多cookies都是在响应头里面设置的:
就有的时候你登陆之后获取一次cookie, 然后光靠这个cookie如果要抓取更里面的数据可能会抓不到,因为正常人访问不是一下直接访问到最后,而是一个网站一个网站的往里面点进去,然后每点一个网站它都会给你设置cooke,这些cookie对于访问更后面的网站必不可少的。
比如我爬我学校官网就是这样,光靠登陆获取的cookie访问不到成绩,必须要模拟登录两次,第一次登陆到主界面获取一次cooke,第二次登陆到成绩网站里面去再获取一次cooke,然后通过这些累加的cooke最后才能直接访问到成绩。
6.响应的cookie
requests库真的特别人性化,利用它的response.cookies直接可以查看别人在响应头给我们设置的cookie:
注意这里的cookies属性不是私有属性,前面不需要加_。
可以看到访问这个网站,别人确实给我们设置了cookie,当然大多数网站不会设置cookie,登陆网站,或者不想让你这么容易爬到数据就可以设置很多cookie,这时候你就必须要一个网站一个网站去登陆获取保存cookie,具体要登陆哪些网站通过抓包分析。
7.响应的状态码:
所谓响应码就是服务器返回给你的一个判别码,不同码有不同的含义,200是大家最想看到的,代表成功访问,还有很多别的,比如404not found, 就是没找到服务器等等,这里发个百度到的响应状态码表:
大概这么多,记住几个常用的就ok了。
当然我们程序返回的是200:
我们初始传递的只是:
requests帮我们拼接后变成了:
而且在拼接的过程中requests还帮我们把非urlencoded类型的数据自动转译了,不需要我们像用urllib.request库那样手动用 urllib.request.urlencode来手动把字典格式数据转成urlencoded格式。
大概要学习的reponse对象就这么多。
剩下的爬取就比较简单了。
因为别人返回的直接是json,直接转成字典就欧克了:
注意结果列表里每一个元素都是个字典:
包含了很多键,也有很多url,我们用哪个呢?英语好的一看thumb就是拇指的意思,意思就是这个url装的肯定不是高清原图,肯定是压缩变小后的图,不清晰,不能用这个,middleurl和拇指url一样最好不用。
我们最后就发现了一个个objurl,这个其实就是最终的答案,但是为什么它长得不像url?-------->>>>>原因就是百度给他用特定的算法加密了,不想让你这么轻松的爬到数据,然后我首先知道它的解密算法肯定隐藏在某个js文件中,后来我好像找到了,可能是这个:
这个js文件 好多url名称和关键字的映射:
但是他写的实在是太复杂了,好多好多函数,我没分析出来。
直接用就好了,这个加密算法其实就是一些字母的映射。
清楚了这些,就好搞了,总结下,返回类型json,导入python字典,获取data键下的列表,里面是所有元素,然后在每个元素里找出objurl的键在用解密算法解密就可以了。 运行结果:
源代码:
# coding='utf-8'
import re
import requests
from lxml import etree
import os
class Spider:
'''用来爬取百度图片同时学习response对象的用法'''
def __init__(self, url='', path=''):
self.url = url if url else 'https://image.baidu.com/search/acjson'
self.path = path if path else './百度图片/'
# 爬哪一页
self.page = 0
# 爬啥
self.name = input('输入关键字:')
# 图片格式
self.image_fmt = ['.jpg', '.png', '.gif', '.jpeg',]
# 拼接参数
self.params = {
'tn': 'resultjson_com',
'ipn': 'rj',
'word': self.name,
'pn': self.page,
}
# 下载失败的次数
self.fail_times = 0
# 用户输入下载数
self.input_number = int(input('输入下载数量:'))
# 用来对加密的百度图片解码,,网上搜的,我分析了半天实在没分析出来
def decode_url(self, url):
"""
对百度加密后的地址进行解码\n
:param url:百度加密的url\n
:return:解码后的url
"""
table = {'w': "a", 'k': "b", 'v': "c", '1': "d", 'j': "e", 'u': "f", '2': "g", 'i': "h",
't': "i", '3': "j", 'h': "k", 's': "l", '4': "m", 'g': "n", '5': "o", 'r': "p",
'q': "q", '6': "r", 'f': "s", 'p': "t", '7': "u", 'e': "v", 'o': "w", '8': "1",
'd': "2", 'n': "3", '9': "4", 'c': "5", 'm': "6", '0': "7",
'b': "8", 'l': "9", 'a': "0", '_z2C$q': ":", "_z&e3B": ".", 'AzdH3F': "/"}
url = re.sub(r'(?P<value>_z2C\$q|_z\&e3B|AzdH3F+)', lambda matched: table.get(matched.group('value')), url)
return re.sub(r'(?P<value>[0-9a-w])', lambda matched: table.get(matched.group('value')), url)
# 根据给定的url获得它的etree对象
def get_etree(self, url):
pass
# 根据给定的待解析数据target和格式字符串fmt解析数据
def parse_data(self, target, fmt=''):
return
# 根据给定url,利用requests库获得它的response对象
def get_response(self, url, params=''):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36',
}
cnt = 0
total_times = 10
while True:
# 如果一分钟还没有数据就退出
if cnt >= total_times:
print('*************\n\n由于各种原因,第{}张图下载失败\n\n**************'.format(self.image_number))
self.image_number += 1
self.fail_times += 1
return None
try:
if params:
response = requests.get(url, params=params, headers=headers,
timeout=2)
else:
response = requests.get(url, headers=headers, timeout=2)
return response
except Exception as e:
cnt += 1
print('可能是网络问题,正在进行第{}次尝试..(一共尝试{}次.)'.format(cnt, total_times))
# 根据response对象测试它的各个属性
def test_response(self, response):
# 测试text (是程序猜测的编码格式之后用猜测的编码格式解码)
response.encoding = 'utf-8'
text = response.text
# print(text)
# 测试content(返回值bytes类型)
content = response.content
# print(content)
# 查看请求头
request_headers = response.request.headers
# print(request_headers)
# 请求的cookie
request_cookies = response.request._cookies
# print(request_cookies)
# 响应头
response_headers = response.headers
# print(response_headers)
# 直接查看响应头的cookie
response_cookies = response.cookies
# print(response_cookies)
# 查看响应状态码
response_code = response.status_code
# print(response_code)
# 查看完整请求url
complete_url = response.url
# print(complete_url)
# 给根据给定的图片url将图片下载到本地
def download(self, obj_list, path=''):
for each in obj_list:
try:
temp_str = each[each.rfind('.'):]
if temp_str in self.image_fmt:
end_fmt = temp_str
else:
end_fmt = self.image_fmt[0]
if os.path.exists(path+str(self.image_number)+end_fmt):
print('路径下已存在{}:{}'.format(self.image_number, each))
if self.image_number >= self.input_number:
self.flag = False
break
self.image_number += 1
continue
except Exception as e:
end_fmt = self.image_fmt[0]
response = self.get_response(each)
if response:
with open(path+str(self.image_number)+end_fmt, 'wb') as f:
f.write(response.content)
print('成功下载{}:{}'.format(self.image_number, each))
if self.image_number >= self.input_number:
self.flag = False
break
self.image_number += 1
else:
return
print('\n^^^^&&^^^^{}张图片下载完毕!,成功下载了{}张, 下载失败了{}张..'.format(self.input_number, self.image_number-self.fail_times, self.fail_times))
os._exit(0)
# 给一个json对象,返回url构成的列表
def download_from_json(self, data):
url_list = []
for each in data['data']:
try:
url_list.append(self.decode_url(each['objURL']))
except Exception as e:
pass
return url_list
# 类从这里开始
def start(self):
# 正在下载第几张图片
self.image_number = 1
# target = self.get_etree(self.url)
# result_list = self.parse_data(target)
# self.download(result_list)
# self.test_response(response)
path = self.path+self.name+'/'
if not os.path.exists(path):
os.makedirs(path)
while True:
response = self.get_response(self.url, self.params).json()
url_list = self.download_from_json(response)
self.download(url_list, path)
self.page += 30
self.params['pn'] = self.page
Spider().start()