实用利器:手把手教你写Python百度图片多模式爬虫 支持缩略图和高清大图批量下载
前言
上一篇《十分钟玩转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
- 收藏
- 分享
- 文章举报
- 一步步分析百度音乐的播放地址,利用Python爬虫批量下载
- 一步步分析百度音乐的播放地址,利用Python爬虫批量下载
- 新一配:perl循环调用python爬虫批量下载喜马拉雅音频
- Python爬虫之高清壁纸下载
- Python 实用爬虫-04-使用 BeautifulSoup 去水印下载 CSDN 博客图片
- Python 实用爬虫-04-使用 BeautifulSoup 去水印下载 CSDN 博客图片
- 利用Python爬虫实现网页图片批量下载
- python网页爬虫实战:PEER数据库地震波批量下载 !
- 利用Python爬虫批量下载网易云音乐歌单歌曲
- Python 3 爬虫之批量下载字帖图片
- 从零开始写Python爬虫 --- 1.7 爬虫实践: 排行榜小说批量下载
- 批量下载小说网站上的小说(python爬虫)
- 用Python 爬虫批量下载PDF文档
- Python实现简单爬虫功能--批量下载百度贴吧里的图片
- 利用Python爬虫批量下载百度图库图片
- Python老司机手把手带你写爬虫,整站下载妹子图,一次爽个够!
- 手把手教你爬取并下载英雄联盟所有英雄皮肤高清大图
- python爬虫:批量下载qq空间里的照片(一)
- Python老司机手把手带你写爬虫,整站下载妹子图,一次爽个够!
- 用python爬虫批量下载pdf