Python软件技巧

用Python爬虫批量下载百度图片

2020-05-24  本文已影响0人  zhyuzh3d

怎样用Pyhon编写爬虫程序,下载1000张鹿晗图片?其实很简单,不到50行代码就能实现任意关键词的批量图片抓取。

编码工具

推荐安装Anaconda,然后使用它自带的JupyterNotebook即可。其次推荐使用VS Code工具,也可以使用Pycharm或者其他。

地址分析

先打开百度图片搜索“鹿晗”,可以搜到很多小图,往下滚动页面的时候会自动加载新的一批小图,所以第一步是怎么一批批的搞到小图。点击小图可以打开大图,真正大图的链接地址是附在小图上的。所以第二步我们要找到每个小图上面的大图链接地址,然后下载这个大图保存。

谷歌浏览器中,页面图片上【右键-检查】打开开发工具,选【Network】选项卡,点击红色按钮右边的那个禁止按钮清空记录。然后鼠标往下滚动,可以看到出现很多新的信息,带你【Type】类型我们只找xhr格式的,点击看一下。

点击一个xhr邮件可以复制这个链接的地址,如下图所示。

可以把这个地址粘贴到Notebook里面仔细看一下,如下图,每个xhrpn值不同,总是30-60-90-120这样变化,就是说我们每次往下滚动一下,就出现一个xhr请求,每个xhr请求会获取30个小图。除了pn还有两个乱码的搜索词queryWord,word,它们的乱码就是中文的鹿晗。另外一个有趣的是face,如果它是1那么就只抓去有人脸的图片,只有后脑勺的图片不要。

回到浏览器开发工具,点击【Preview】数据预览,可以看到data下面有大概30个数据,每个数据代表一个鹿晗小图信息。

仔细看其中一个信息,并把地址类型的文字复制到浏览器窗口查看,就会发现真正的大图链接地址在【replaceURL】的第2个(1号)数据的【ObjectURL】,如下图所示的那个地址就是大图地址。

抓一次30张的函数

先写一个批量抓取的函数getBNaiduImgs(word,pn,face=0),这个函数的作用就是根据pn抓取30张鹿晗图片,当然也可以是吴亦凡图片,关键看word

import time
import requests
import json
import urllib
import os

def getBaiduImgs(word,pn,face=0):  #根据不同的pn数字获取并保存批量图片
    url = f'http://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord={word}&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=&z=9&ic=&hd=&latest=&copyright=&word={word}&s=&se=&tab=&width=0&height=0&face={face}&istype=&qc=&nc=1&fr=&expermode=&force=&pn={pn}&rn=30&gsm=3c&1590168124018='#复制百度图片网页开发面板Network里的xhr对应的RequestURL类似地址,替换pn={pn}word={word}
    folderPath=f'{os.getcwd()}/imgs/{word}/'
    if not os.path.exists(folderPath):
        os.makedirs(folderPath)
    
    res = requests.get(url, timeout=3)  #获取上面地址对应的批量缩略图数据    
    resjson = ''
    try:
        resjson = json.loads(res.text.replace('\\\'',''))  #解析批量缩略图数据
    except ValueError:
        print(f'>>JSON ERR:{ValueError}')
        return

    for m in resjson['data']:  #针对每个缩略图信息执行下面内容
        #为了防止出错,要检查各个数据都正常存在
        if ('replaceUrl' in m) and (len(m['replaceUrl'])>1) and ('ObjURL' in m['replaceUrl'][1]):
            imgurl = m['replaceUrl'][1]['ObjURL'].split('?')[0]  #获取真正的图片地址
            fname = str(random.random()).replace('0.','')+'.jpg' #随机文件名
            if imgurl.split('/')[-1]: 
                fname=imgurl.split('/')[-1]  #图片地址中最后一个斜杠后面的,就是图片文件名,类似http:.../adf.jpg
                if fname.find('.')==-1:
                    fname=fname+'.jpg'
            print(f'正在抓取:{imgurl}') 
            
            try:
                imgres = requests.get(imgurl,timeout=3)
                if sys.getsizeof(imgres.content)>15000:
                    with open(f'imgs/{word}/{fname}','wb') as f:  #打开即新建一个图片文件,wb,write binnary
                        f.write(imgres.content)  #写入文件,即保存
                        time.sleep(1)  #获取每张图片后停一下
            except Exception as e:
                msg=e.message if (hasattr(e, 'message')) else e
                print(f'>>抓取失败:{imgurl}\n{msg}')
                pass 
    return url

上面的代码里有很多注释,这里就不重复解释啦。总之,有很多图片链接其实已经失效是打不开的,还有很多是假的图片,比如那种白色的网站占位小图,所以加了很多判断,还判断了图片的大小要大于15k才保存。

总之,虽然有30个链接,但可能抓到的合格图片只有十几张。

如果要明确限制抓1000张结束,那么就不能根据小图链接的数量判断结束抓取,而是根据文件夹中图片的数量来判断。但还是会有不少不合格的图片,比如白图或者抓到了关晓彤的图片。

批量抓取

这个代码很简单,不解释。

for i in range(0, 120, 30): #[0,30,60,90,120]
    print('>> GETTING', i)
    getBaiduImgs('鹿晗',i,1) #使用上面的函数
print('>> ALL OK!')

运行起来的话偶尔会有抓取失败的情况,慢慢等十几分钟就能够下载上千张图片啦!

那个xhr地址其实还有更多参数,比如限制宽高weight,height之类,大家可以自己尝试探索。


欢迎关注我的专栏( つ•̀ω•́)つ【人工智能通识】

每个人的智能新时代

如果您发现文章错误,请不吝留言指正;
如果您觉得有用,请点喜欢;
如果您觉得很有用,欢迎转载~

上一篇下一篇

猜你喜欢

热点阅读