因为需要一些图片素材,又不想一个个手动下载,遂通过爬虫来解放双手。在百度图片中搜索“汉服美女”,然后以浏览器地址栏上的地址作为初始 URL。通过对 URL 分析知道 URL 分为 3 部分:域名 + 固定参数 + 关键字参数。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @author: Nancy
# @contact: [email protected]
# @software: PyCharm
# @file: getHanfu.py
# @time: 2019/2/23 14:34
import requests
import re
import time
class BaiduPictures(object):
def __init__(self, keyboard):
self.headers = {"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3355.4 Safari/537.36"}
self.base_url = "https://image.baidu.com"
self.keyboard = str(keyboard)
def send_request(self, url):
"""
:param url: 网址
:return: unicode 型数据
"""
try:
html = requests.get(url, headers=self.headers).text
return html
except Exception as e:
print(e)
def make_request(self, url):
"""
:param url: 网址
:return: bytes 型数据 (二进制的数据)
"""
try:
html = requests.get(url, headers=self.headers).content
return html
except Exception as e:
print(e)
def load_page(self, html):
pattern = r'"objURL":"(http.*?)",'
img_urls = re.findall(pattern, html)
for img_url in img_urls:
img_puffix = img_url.rsplit(".", 1)[1].lower()
t = time.time()
now_time = str(round(t * 1000))
data = self.make_request(img_url)
if data and img_puffix in ['jpg', 'jpeg', 'png']:
self.write_pic(data, now_time + "." + img_puffix)
elif data:
self.write_pic(data, now_time + ".jpg")
else:
print(img_url + u"地址无效")
# data = self.make_request(img_url + ".jpg")
# if data:
# self.write_pic(data, now_time + ".jpg")
def write_pic(self, data, filename):
print(u"[INFO]: 正在下载:%s..." % filename)
with open(u"E:\images\img" + filename, 'wb') as f:
f.write(data)
def start(self):
html = self.send_request(self.base_url + "/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1" +
"&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0" +
"&istype=2&ie=utf-8&fm=index&pos=history&word=" + self.keyboard)
self.load_page(html)
if __name__ == "__main__":
keyboard = input("请输入关键词:")
Pictures = BaiduPictures(keyboard="汉服美女")
Pictures.start()
通过上方代码,我们会发现只能下载 30 张图片,这是因为百度图片是动态加载的,它的网页原始数据其实是没有这个图片的,通过运行 JavaScript,把这个图片数据插入到网页的 html 标签中,那这样造成的结果是,我们在开发者工具中虽然能看到这个 html 标签,但实际上,当我们在看网页的原始数据的时候,其实是没有这个标签的,它只在运行时加载和渲染,那这个时候怎么办呢?怎么把这个图片给下载下来呢?这里面我们就换一个思路:抓包。
按F12打开开发者工具,点击 Network–XHR,然后向下滑动滚动条,会一直出现一个名为:acjson?tn=resultjson_com…
的请求,点击它再点击 Preview,会发现这是 json 数据,点开 data,会看到里面有 30 条数据,每一条都对应着一张图片。
这下明白了吧,百度图片一开始只加载 30 张图片,当我们往下滑动滚动条时,页面会动态加载 1 条 json 数据,每条 json 数据包含 30 条信息,信息里又包含图片的 URL,JavaScript 会将这些 URL 解析并显示出来。这样,每次滚动到底就又多出 30 张图片。
那么,这些一直出现的 json 数据有什么规律呢?我们点击 Headers,然后对比这些 json 数据的头部信息。通过对比,我们发现 Headers 下的 Query String Parameters 中的字段大多保持不变,只有 pn、gsm 字段不同,而 pn 每次递增 30,以及最后的时间戳不断变化。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @author: Nancy
# @contact: [email protected]
# @software: PyCharm
# @file: getHanfu.py
# @time: 2019/2/23 14:34
import requests
import re
import time
class BaiduPictures(object):
def __init__(self, keyboard):
self.headers = {"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3355.4 Safari/537.36"}
self.base_url = "https://image.baidu.com"
self.keyboard = str(keyboard)
def send_request(self, url):
"""
:param url: 网址
:return: unicode 型数据
"""
try:
html = requests.get(url, headers=self.headers).text
return html
except Exception as e:
print(e)
def make_request(self, url):
"""
:param url: 网址
:return: bytes 型数据 (二进制的数据)
"""
try:
html = requests.get(url, headers=self.headers).content
return html
except Exception as e:
print(e)
def load_page(self, html):
pattern = r'"objURL":"(ippr.*?)",'
encrypted_urls = re.findall(pattern, html)
for encrypted_url in encrypted_urls:
t = time.time()
now_time = str(round(t * 1000)) # 时间戳
img_url = self.url_complie(encrypted_url) # 解密 objURL 地址
img_puffix = img_url.rsplit(".", 1)[1].lower() # 图片格式后缀
data = self.make_request(img_url)
if data and img_puffix in ['jpg', 'jpeg', 'png']:
self.write_pic(data, now_time + "." + img_puffix)
elif data:
self.write_pic(data, now_time + ".jpg")
else:
print(img_url + u"地址无效")
# data = self.make_request(img_url + ".jpg")
# if data:
# self.write_pic(data, now_time + ".jpg")
def url_complie(self, url):
"""
:param url: 加密的 objURL 地址
ex: ippr_z2C$qAzdH3FAzdH3Ft42_z&e3Bh7p55b_z&e3Bv54AzdH3F7rs5w1AzdH3Ft4w2jAzdH3Fm90nlb0mAzdH3Fdc_lmaxc9a_z&e3B3r2
:return: 解密 URL
"""
res = ''
c = ['_z2C$q', '_z&e3B', 'AzdH3F']
d = {'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': '/'}
if url is None or 'http' in url:
return url
else:
j = url
for m in c:
j = j.replace(m, d[m])
for char in j:
if re.match('^[a-w\d]+$', char):
char = d[char]
res = res + char
return res
def write_pic(self, data, filename):
print(u"[INFO]: 正在下载:%s..." % filename)
with open(u"E:\images\img" + filename, 'wb') as f:
f.write(data)
def start(self):
for page in (30, 121, 30):
html = self.send_request(self.base_url + "/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=" + self.keyboard +
"&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&hd=&latest=©right=&word=" + self.keyboard +
"&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=" + str(page) +
"&rn=30&gsm=1e&1551881855622=")
print(self.load_page(html))
if __name__ == "__main__":
keyboard = input("请输入关键词:")
Pictures = BaiduPictures(keyboard="汉服美女")
Pictures.start()
python requests的content和text方法的区别
python百度图片爬取
python学习(7):python爬虫之爬取动态加载的图片,以百度图片为例
百度返回的JSON数据解析返回的objURL(python版本)