您的位置:首页 > 编程语言 > Python开发

实用利器:手把手教你写Python百度图片多模式爬虫 支持缩略图和高清大图批量下载

2020-03-31 19:24 796 查看

前言

      上一篇《十分钟玩转Flex布局》发布之后,我分享给了大一大二学弟之后,他们给我聊天说确实学到东西了,我听了还是很开心的,初心达到了。

      ε=(´ο`*)))唉,我已经好久没更新啦。

     不是我偷懒在家睡觉,而是最近真的太忙了,事情一件接一件的来。其实最近做东西的时候遇见了好多问题,早都想总结分享一下啦,可是真的没有时间,寻找实习岗位、学校里的事、域名备案被退了好几次、小程序客户端和服务端......等等等每天晚上都是撸代码到深夜。昨天晚上微信小程序搞完,服务端部署到服务器上之后,才能有点时间在这写点东西。O(∩_∩)O哈哈~ 啰嗦了

    好了进入正题。前两天,在做微信小程序测试的时候,需要一些小朋友的照片存服务器上,无奈我的图片文件夹都是一些代码截图,我就自己简单写了一个python脚本爬了一些网图就丢服务器上了。昨天晚上做生成用户小程序海报模块的时候如下图,发现用户头像很模糊,最初我以为是canvas的问题,反复在前端代码里面找问题,缩小头像比例,无果!最后到服务器上一看图片大小1.5kb左右。。。。问题就找到了,我爬的是缩略图图片质量很低,再经过拉伸,canvas绘图,图片就模糊了。

  于是乎,今天上午,重新写了一下python代码,支持缩略图和高清大图两种模式图片的批量下载。

  我把整体的流程分析一下,当然啦,也可以直接拿着源代码去奔放!

分析过程

一、分析网页

①在百度图片中敲入关键字搜索(例如:fighting)图片

 首页展现了30张图片,这一看就是瀑布流式布局,随着滚动条下拉,图片往后慢慢叠加。所以肯定是通过ajax发送异步请求向服务器获取数据的。

②分析请求

我们F12后,network选中xhr,随着滚动条的移动,不断地在发送新的异步请求。

③分析请求报文

  

我们得出以下结论:1.get请求 2.关键参数  queryWord word关键词  pn、rn与数量有关

解释:rn是每页请求的个数(每次最多60张,我试过了),pn是总量,第二页就相当于pn=2*rn,实际是页面上的第三页,最开始有30张图片了,最后一个参数是时间戳,其他参数不用管,一并发送即可

我们都知道,get请求时QueryString传值,参数跟url后面,所以我们只需要https://image.baidu.com/search/acjson,之后添后面的参数就行了

④分析response数据

我们还是把数据(对应ajax请求的response里面)放到json.cn中看一下。

很明显,我们需要的数据在data里面,缩略图地址thumbURl,原图地址objURL。

我咋知道的?点进去试试嘛 O(∩_∩)O哈哈~objURL是点不动的【加密的】,这个后面再说

至此,分析完毕,思路清晰,模拟发送get请求的数据,清洗数据,图片下载到本地,over!

代码实现

  准备工作 

 就这四个库,后三个都是内置的

[code]import  requests
import  json
import  time
import  os

①获取图片地址

   代码分析:

  代码:

[code]import  requests
import  json
url="https://image.baidu.com/search/acjson"
request_args={"tn":"resultjson_com","ipn":"rj","ct":"201326592","is":"","fp":"result","queryWord":"fighting","cl":2,"lm":-1,"ie":"utf-8",
"oe":"utf-8","adpicid":"","st":-1,"z":"","ic":0,"hd":"","latest":"","copyright":"","word":"fighting","s":"","se":"","tab":"",
"width":"","height":"","face":"0","istype":2,"qc":"","nc":1,"fr":"","expermode":"","force":"","pn":30,"rn":30,
"gsm":"1e","1585198294921":"",
}
headers={
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
,"Referer":"https://image.baidu.com"
}
response=requests.get(url=url,headers=headers,params=request_args)
if response.status_code==200:
data_list=json.loads(response.text)["data"]
data_list=list(filter(lambda x:x.keys().__len__()>0,data_list))
for item in data_list:
print(item["thumbURL"])

 效果:

下面我们只需存盘就行了,但是我们现在得到的是缩略图的地址,我们还有原图地址没有获取呢

②获取原图真实地址

 我用了“真实”,可能都已经猜到了,要用一些手段才能得到想到的地址。

我们打印objURL发现,是一段看不懂的字符串,我最初以为是base64但是验证了一下不是,于是另辟蹊径,去看看前端有没有相应的js去处理的。

于是乎,我扒拉了半天,过程省略,终于找到了与编码有关的JS

   

我去!又是看不懂的东西!实际这为了优化前端性能,对js进行了压缩。

我用vscode格式化了一下,只放有用的部分看一下吧

[code]var u = function (e) {
var r = a.getSearchConf(),
n = new Date,
t = n.getTime();
if (void 0 == r.fmq) e.fmq.value = t + "_R";
else if (r.fmq.indexOf("m") > -1 && -1 == r.fmq.indexOf("_m") && -1 == r.fmq.indexOf("_R")) {
var f = r.fmq;
e.fmq.value = t + "_" + f + "_R"
} else e.fmq.value = t + "_R";
return e.fm.value = void 0 == r.fr || "" == r.fr ? "detail" : r.fr, !0
},
c = {
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: "/"
},
s = /([a-w\d])/g,
m = /(_z2C\$q|_z&e3B|AzdH3F)/g;
a.uncompile = function (e) {
var r = e.replace(m, function (e, r) {
return c[r]
});
return r.replace(s, function (e, r) {
return c[r]
})
}

那一串k/v形式的对象就是编码规则,只要知道编码格式就可以进行译码了

定义译码函数:

[code]def encode_objurl(objurl):
code_dic= {
"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",
}
objurl=objurl.replace("_z2C$q",":").replace("_z&e3B",".").replace("AzdH3F","/")
res=""
for c in objurl:
if c in code_dic.keys():
res+=code_dic[c]
else:
res+=c
return res

效果:

我们调用函数之后,把刚刚加密的url地址进行了解密得到了最终的原图地址,现在我们只剩最后一步了,保存图片到本地。

③保存图片到本地

[code]ext_name=imgsrc[imgsrc.rindex("."):].split("?")[0]#扩展名
path="./"+keyword+"_imgs/"
if not os.path.exists(path):
os.mkdir(path)
filename=path+keyword+str(index)+ext_name
try:
with open(filename,"wb") as wstream:
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
}
try:
imgresponse = requests.get(url=imgsrc, headers=headers,timeout=4)
wstream.write(imgresponse.content)
print(filename)
index += 1
except Exception:
print("网络迷失了方向")
except Exception:
print("出错啦,写入失败")

说明:

这段代码是从写好的完整代码中粘贴出来的,只是写入图片部分的代码。imgsrc是得到的路径,index是一个标识

将网络图片保存到本地,同样用到requests但是要注意要用response.content进行文件读写操作。同时要进行相应的异常处理,因为我们获取原图时是解密之后再请求的,可能会有解密错误的情况,所以要设置请求超时的时间,并做异常处理。

好了至此,所有主要功能都实现完了,之后再加上相应的逻辑处理就可以了。

完整代码

Github地址:https://github.com/Young-coder-wrz/PythonScript/tree/master 

图片保存地址默认为当前路径下的一个文件夹,可以更改path参数,自行设置

[code]import  requests
import  json
import  time
import  os
def encode_objurl(objurl):
code_dic= {
"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",
}
objurl=objurl.replace("_z2C$q",":").replace("_z&e3B",".").replace("AzdH3F","/")
res=""
for c in objurl:
if c in code_dic.keys():
res+=code_dic[c]
else:
res+=c
return res

def DownloadImg(keyword,mode,numbers):
if mode.lower() not in ["small","big"]:
print("模式输入错误!")
return
if int(numbers)>2000:
print("太多了,扛不住!")
return
url = "https://image.baidu.com/search/acjson"
pages=numbers//60 if numbers%60==0 else numbers//60+1
pindex=1
index = 1
for pindex in range(1,pages+1):
request_args = {
"tn": "resultjson_com",
"ipn": "rj",
"ct": "201326592",
"is": "",
"fp": "result",
"queryWord": keyword,
"cl": 2,
"lm": -1,
"ie": "utf-8",
"oe": "utf-8",
"adpicid": "",
"st": -1,
"z": "",
"ic": 0,
"hd": "",
"latest": "",
"copyright": "",
"word": keyword,
"s": "",
"se": "",
"tab": "",
"width": "",
"height": "",
"face": "0",
"istype": 2,
"qc": "",
"nc": 1,
"fr": "",
"expermode": "",
"force": "",
"pn": pindex * 60,
"rn": 60,
"gsm": "1e",
"1585198294921": "",
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
, "Referer": "https://image.baidu.com"
}
response=requests.get(url=url,headers=headers,params=request_args)
if response.status_code==200:
data_list=list(filter(lambda x:x.keys().__len__()>0,json.loads(response.text,encoding="utf-8")["data"]))
for item in data_list:
imgsrc=item["thumbURL"] if mode.lower()=="small" else encode_objurl(item["objURL"])
ext_name=imgsrc[imgsrc.rindex("."):].split("?")[0]#扩展名
path="./"+keyword+mode.lower()+"_imgs/"
if not os.path.exists(path):
os.mkdir(path)
filename=path+keyword+str(index)+ext_name
try:
with open(filename,"wb") as wstream:
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
}
try:
imgresponse = requests.get(url=imgsrc, headers=headers,timeout=4)
wstream.write(imgresponse.content)
print(filename)
index += 1
except Exception:
print("网络迷失了方向")
except Exception:
print("出错啦,写入失败")
print("保存了" + str(pindex) + "页," + str(index-1) + "张图片")
time.sleep(2)
else:
print("网络迷失了方向")
return

if __name__ == '__main__':
#author:睿吉吉
#date:2020年3月26日
#version:1.0.0
print("**********************Image download script**********************")
keword=input("请输入要下载的图片关键字:")
mode=input("请输入模式:small or big? [small 缩略图(速度杠杠滴)  big高清大图(略慢,质量杠杠滴)]")
try:
numbers=int(input("请输入图片下载数量:"))
print("任务创建成功->关键字:"+keword+"   数量:"+str(numbers))
DownloadImg(keword,mode,numbers)
print("下载完毕,over!")
except Exception:
print("不要乱输入,不让你下了,拜拜┏(^0^)┛")
exit(0)

 效果演示

缩略图:

高清原图:

缩略图比较小,也不用解密下载的比较快,300张大概20~30秒。高清原图文件本来就大,还要进行一次解密,300张大概2分钟左右,我觉得还可以啦,很好用

        好啦,跟大家一步步解析了 ,从网页到请求头到代码实现,应该很清楚了吧,大多是基本操作叭^_^

        我也看了同类文章,大多都是直接解析了原网页一次取30张图片,并没有好好去分析啊,30张那能行,来还不多来点,不能客气啊!哈哈

        大家可以跟着写一写试试或者直接拿去用都行,开心就好,我很认真去写我的每一篇博客,为了让看到我的博客的人能够收获到东西,觉得好用不妨点个赞、点个关注谢谢啦

  • 点赞 5
  • 收藏
  • 分享
  • 文章举报
睿吉吉 发布了11 篇原创文章 · 获赞 68 · 访问量 1万+ 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: