前言
现在百度图片加载并不是一页一页地加载出来的,直接请求得到的 HTML 代码里面,并没有我们需要的数据,因为这些信息是通过Ajax加载的,并且通过js渲染生成的。在爬取动态页面时,我们就需要分析这个网页的请求了。
大家可以先跳到代码部分先执行,观看效果
分析Ajax请求
打开百度图片搜索页面:地址
我们可以看到这个网页并没有翻页按钮,而当我们一直往下拉请求,网页会自动的给我们加载出更多内容,显然我们只能获得到第一页的html内容。
那我们要怎么获得所有页的数据呢?
我们在Chrome中打开开发者工具(F12)。我们点击Network,点击XHR标签。
然后我们刷新网页,将图片继续下拉。这个时候我们就可以看到XHR标签,在网页每一次加载的时候就会跳出一个请求(acjson…),点击它再点Preview,我们看到这是一条json数据,点开data,我们看到这里面有30条数据,每一条都对应着一张图片。
返回上图所示页面,我们点击第一个请求,点击Headers,往下拉看到Query String Parameters,可以看到他的参数如下:
tn:resultjson_com
ipn:rj
ct:201326592
is:
fp:result
queryWord:spyder
cl:2
lm:-1
ie:utf-8
oe:utf-8
adpicid:
st:-1
z:
ic:0
hd:
latest:
copyright:
word:spyder
s:
se:
tab:
width:
height:
face:0
istype:2
qc:
nc:1
fr:
expermode:
force:
pn:30
rn:30
gsm:1e
一个地址就是由一个基础网址加上如上图的参数组成,如下图所示
https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=spyder&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&hd=&latest=©right=&word=spyder&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=30&rn=30&gsm=1e
queryWord和word就是我们搜索的值,pn的值即是刷新出来的图片数量,我们会发现每一个请求的pn参数都会加30。
当搜索关键字是中文时,比如我搜索大学
可以看到,queryWord和word的 值都变成%E5%A4%A7%E5%AD%A6了,啥情况呢,我要的是大学,怎么变成这个东西了呢?
原来, 还需要一顿操作:
>>>import urllib
>>>keyword="大学"
>>>keyword=urllib.parse.quote(keyword)
>>>keyword
'%E5%A4%A7%E5%AD%A6'
这样我们就成功找到我们需要的信息位置了,我们可以在请求头中看到存放json数据的url地址,终于可以开始了
import urllib
def get_urlset(keyword,page):#获取存放json数据的url地址,keyword即是输入的值,page为获得的url地址的个数
url_list=[]
kw=urllib.parse.quote(keyword)
for j in range(page):
p=j*30
url_list.append("https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord="+kw+"&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&hd=&latest=©right=&word="+kw+"&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn="+str(p)+"&rn=30&gsm=b4")
return url_list
获得网址内容
加headers是为了伪装成浏览器访问。
加proxies是为了加代理,免费的代理只能维持一会可能就没用了,自行更换。
import requests
def get_html(url):#解析网址
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
proxies = {'http': '117.91.250.131:9999'}
try:
resp = requests.get(url, headers=headers)
except:
resp = requests.get(url, headers=headers, proxies=proxies)
return resp
利用上述方法get_html(url).text获得文本内容,get_html(url).content获得二进制代码,我们到此为止还没有用到。
解析json
我们点击第一个请求,点击Preview,打开其中的一个data,我们前面说过每一条data都对应一张图片,打开data,果然在hiverURL我们看到了图片地址
好了,知道图片具体在哪了,用如下代码获得图片地址
import json
def parse_json(text):#解析json获得图片地址
img_url=[]
try: #多用用try,防止加载失败,程序退出
result=json.loads(text)
if result:
for i in result.get("data"):
img_url.append(i.get("hoverURL"))
return img_url
except:
print("获取图片地址失败")
创建文件夹
大家爬出来的图片肯定不希望散乱着弄出来,想要要用一个子文件夹包起来。如图所示。
这里的“E:/学习/爬虫/百度图片”,是根目录,在跑程序之前要提前创建好哟,路径大家自行修改。
如何创建子文件夹呢,如下:
def mkdir(path):
# os.path.exists(name)判断是否存在路径
# os.path.join(path, name)连接目录与文件名
isExists = os.path.exists(os.path.join("E:/学习/爬虫/百度图片", path))
if not isExists:
print('makedir', path)
os.makedirs(os.path.join("E:/学习/爬虫/百度图片", path))
os.chdir(os.path.join("E:/学习/爬虫/百度图片", path))
return True
else:
print(path, 'already exists')
return False
保存图片
def open_img(img,Number):#打开并保存图片到文件中
if img:
filename=keyword+str(Number)+".jpg"
with open (r"E:/学习/爬虫/百度图片/"+keyword+r'/'+filename,'wb') as f:
try:
f.write(get_html(img).content)
print(filename+"下载成功")
except:
print("读取文件失败")
最后获得的文件及文件名如下图所示:
代码
在跑程序之前要把保存图片的文件夹创建好哟,我的路径是"E:/学习/爬虫/百度图片",大家自行修改。还有就是可能如下四个包某些包要自行导入,我用的Anaconda 的spyder已经都导入好了,可以直接使用。
import urllib
import json
import requests
import os
import time
def get_urlset(keyword,page):#获得网址链接集合
url_list=[]
kw=urllib.parse.quote(keyword)
for j in range(page):
p=j*30
url_list.append("https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord="+kw+"&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&hd=&latest=©right=&word="+kw+"&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn="+str(p)+"&rn=30&gsm=b4")
print(page)
return url_list
def get_html(url):#获得网址内容
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
proxies = {'http': '117.91.250.131:9999'}
try:
resp = requests.get(url, headers=headers)
except:
resp = requests.get(url, headers=headers, proxies=proxies)
return resp
#text=get_html(get_urlset("湖北中医药大学")[0].text)
def parse_json(text):#解析json获得图片地址
img_url=[]
try:
result=json.loads(text)
if result:
for i in result.get("data"):
img_url.append(i.get("hoverURL"))
return img_url
except:
print("获取图片地址失败")
#print(parse_json(text) )
def mkdir(path):
# os.path.exists(name)判断是否存在路径
# os.path.join(path, name)连接目录与文件名
isExists = os.path.exists(os.path.join("E:/学习/爬虫/百度图片", path))
if not isExists:
print('makedir', path)
os.makedirs(os.path.join("E:/学习/爬虫/百度图片", path))
os.chdir(os.path.join("E:/学习/爬虫/百度图片", path))
return True
else:
print(path, 'already exists')
return False
def open_img(img,Number):#打开并保存图片到文件中
if img:
filename=keyword+str(Number)+".jpg"
with open (r"E:/学习/爬虫/百度图片/"+keyword+r'/'+filename,'wb') as f:
try:
f.write(get_html(img).content)
print(filename+"下载成功")
except:
print("读取文件失败")
def main(keyword,page):#主函数
mkdir(keyword)
urlset=get_urlset(keyword,page)
Number=0#用来计数
for url in urlset:
Text=get_html(url).text
img_url=parse_json(Text)
if img_url:
for img in img_url:
open_img(img,Number)
Number+=1
if __name__ == '__main__':
# 计时
t1 = time.time()
keyword=input("请输入你要爬取的图片:")
page=input("请输入你要爬取的页数:")
page=int(page)
main(keyword,page)
print(time.time() - t1)
运行结果如下