高效爬取美丽的图片

  • 任务目标
  • 任务流程
    • step1:复制
    • step2:粘贴
    • step3:重学异步
      • 什么是异步
      • 勤奋的小明
      • 理解小明
      • 理解~~美女~~ 美丽的图片
      • 明确思路
      • 检验思路
      • 检验结果
    • step4:线程池
      • 步骤
  • 看评论

任务目标

在 一起学爬虫(Python) — 05 中呢,我们爬取了很多很多的美女 美丽的图片,但是不晓得大家有没有觉得太慢了,这样让很快的我们很是接受不了。

正好我们在 一起学爬虫(Python) — 08 中讲到了异步,众所周知,异步是提高效率的一种方式,那么我们是不是也可以用异步来实现唰唰唰地下载美女 美丽的图片呢?

这就是我们今天的任务目标!如果你有思路了,就把你的思路在评论里跟大家分享一下吧,学习还是要多交流的嘛~

(`・ω・´)

任务流程

step1:复制

没错,这里我们又用到了step这个英语单词,为什么呢?

一切尽在不言中…

话不多说,直接开始冲!

找到我们之前在 一起学爬虫(Python) — 05 中编写的代码,直接复制到一个新的py文件里!

当然如果你是那种邻居家的孩子的话,也可以选择自己找一个网站尝试着把图片爬取下来。

这里小泽给大家直接设置几个传送门,方便去看美女 美丽的图片:

传送门①号

传送门②号

两个就好了,多了怕大家身体承受不住哈。

当然如果你属于那种比较难以满足的,不要害羞哦,偷偷地告诉小泽,会悄悄地给你一些好康的,绝对不会暴露你的特殊爱好的,我保证!

(`・ω・´)

step2:粘贴

简简单单两个字,却包含了可大可小,可长可短的很多很多信息,秒啊!

当然如果你懒得翻到 一起学爬虫(Python) — 05 中去寻找美女 美丽的图片的代码呢,也可以直接在下面进行复制然后粘贴哈。

这就是爱吗,小泽好暖!

import requests
from lxml import etree
import os
# 指定第一个url
url = 'http://www.win4000.com/zt/dongman.html'
# 指定伪装头
headers = { 
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
# 读取第一个url
response = requests.get(url=url,headers=headers).text
# 树化第一个页面
tree = etree.HTML(response)
# 找到图片对应的第二个页面(li列表)
leaf = tree.xpath('//div[@class="tab_tj"]//ul[@class="clearfix"]/li/a/@href')

# 创建一个文件夹
if not os.path.exists('./setu'): # 如果该文件夹不存在,则创建
    os.mkdir('./setu')


# 遍历每一张图片所对应的第二个页面
for a in leaf:
    # 防止出错中止程序
    try:
        # 设置局部变量,177105_b
        b = 1
        # 循环获取图集里的每一张图片,默认最多10张
        while b < 11:
            # 切割-->拼接
            c = a.split('.html')[0]
            d = c+'_'+str(b)+'.html'
            # 当前图集的下一张
            b += 1
            # 对第二个页面开始读取,前面的都是为了方便找url
            e = requests.get(url=d,headers=headers).text
            # 树化第二个页面
            f = etree.HTML(e)
            # 在第二个页面里找到我们要的图片地址 [0]是指把列表里第一个元素提取出来,可以这么拼接!!
            g = f.xpath('//div[@class="main"]//div[@class="pic-meinv"]/a/img/@src')[0]
            # 获取图片的二进制数据
            h = requests.get(url=g,headers=headers).content
            # 给图片起名
            i = 'setu/'+g.split('/')[-1]
            # 持久化存储
            with open(i,'wb') as fp:
                fp.write(h)

    except:
        print('出错啦')

不过确实写的有点粗糙哈,如果有能力的小伙伴可以把代码优化一下,然后偷偷发给小泽,偷偷的哦…

(`・ω・´)

step3:重学异步

这里有的同学就问了,老师我们之前明明学过异步,为什么还要重学异步呢?难道是老师忘了吗忘了吗忘了吗…

当然不是啦,说是重学是有原因的,原因就是之前的异步有很多小伙伴压根就没有看懂的嘛,不是说什么笨不笨,聪明不聪明,而是小泽讲的不是很认真吧,所以也就耽误了很多渴望学习的小伙伴,于是乎,今天就来教大家学异步啦!

当然,异步不是那么容易学的,我们现在只能努力入门,然后慢慢积累经验,才能应用得当,这就需要大家一起努力啦!

什么是异步

这里有的同学就又要问了,老师老师老师,这里之前讲过诶,为什么还要再讲一遍呢?

因为今天我们要用美女 美丽的图片的例子来说明,什么是异步,通过实践来理解异步的作用!

(`・ω・´)

勤奋的小明

好的,让我们把目光重新放到还在一心三用的小明身上…

如果你忘了,没关系,这里也不会讲,自己去 一起学爬虫(Python) — 08 里面复习一下子哈。

(`・ω・´)

先不管其他代码,我们看看以下的代码:

理解小明

大家试着这样理解,小明sleep的那两秒或者四秒,是他仅有的休息时间,然后异步的作用就是在他休息的时间让他去干别的事,这样就可以理解了吧!!!

理解美女 美丽的图片

那么我们从网上爬取图片的时候,步骤又是怎么样的呢?

1.向图片网址发送请求,就是模拟我们打开图片网址
2.接收服务器发给我们的所有信息,对,是它自愿给我们的!
3.从很乱的数据里面找到我们要的图片对应的网址,每个图片都有一个对应的下载网址
4.然后进行下载,注意,下载的话是要根据每个人的网速决定快慢的
5.保存到本地

但是但是但是,我们这样做的前提是我们只有一台电脑,不像网吧老板,可以同时下50部高清电影,还不带卡的。

那么我们仔细想一想,电脑在等待图片下载完成的这个过程中,它做了什么吗?

没有!

它什么都没做。

也许一张图片很快就下好了,唰的一下很快啊,一两秒就搞定了,所以你感觉不到电脑在偷懒,但是如果几百几千张的话,积少成多,众所周知,时间就是金钱,相当于我们在浪费金钱!

要做一个勤俭节约的人,所以,不能浪费钱!

那么,我们要怎么做呢?

明确思路

我们要做的,可以是让电脑在等待图片下载好的这段时间里,让它再去下载另一张图片,然后再它等待另一张图片下载好的时候,再让它去下载另另一张的图片……

也就是异步!!!

如果不是异步,就是同步,同步就是按照代码的流程走,不带拐弯的。

那我们是不是可以这样想,需要等待的是下载图片的这个过程:

也就是向网络请求的这一块地方!

为什么不是起名,拼接,保存的那些地方呢?

因为做这些操作的时候所利用到的是电脑的性能,如果电脑的性能不过关的话,怎么也不会快的,但是下载东西不同,是可以多线程的!

思路明确,让我们把小明替换成美女 美丽的图片!

(`・ω・´)

检验思路

首先,将我们源码中的这一部分:

单独拿出来,剩下的全部封装到方法中:

async def meinv():
    # 指定第一个url
    url = 'http://www.win4000.com/zt/dongman.html'
    # 指定伪装头
    headers = { 
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
    }
    # 读取第一个url
    response = requests.get(url=url, headers=headers).text
    # 树化第一个页面
    tree = etree.HTML(response)
    # 找到图片对应的第二个页面(li列表)
    leaf = tree.xpath('//div[@class="tab_tj"]//ul[@class="clearfix"]/li/a/@href')
    # 遍历每一张图片所对应的第二个页面
    for a in leaf:
        # 防止出错中止程序
        try:
            # 设置局部变量,177105_b
            b = 1
            # 循环获取图集里的每一张图片,默认最多10张
            while b < 11:
                # 切割-->拼接
                c = a.split('.html')[0]
                d = c + '_' + str(b) + '.html'
                # 当前图集的下一张
                b += 1
                # 对第二个页面开始读取,前面的都是为了方便找url
                e = requests.get(url=d, headers=headers).text
                # 树化第二个页面
                f = etree.HTML(e)
                # 在第二个页面里找到我们要的图片地址 [0]是指把列表里第一个元素提取出来,可以这么拼接!!
                g = f.xpath('//div[@class="main"]//div[@class="pic-meinv"]/a/img/@src')[0]
                # 获取图片的二进制数据
                h = requests.get(url=g, headers=headers).content
                # 给图片起名
                i = 'setu/' + g.split('/')[-1]
                # 持久化存储
                with open(i, 'wb') as fp:
                    fp.write(h)
        except:
            print('出错啦')

因为是直接复制到了之前小明学习英语的方法里,所以就只改了下函数名,诶嘿。

但是,到这里我们就全部错了!大错特错的那种!!

为什么呢?还记得我们之前说的阴阳人吗,只有阴阳起来,才能融入这个环境,很明显我们的requests模块是个正人君子,也就是说,如果要用异步,我们就没有办法用requests模块,如果要用requests模块就没有办法用异步!这就是不兼容。

检验结果

单从结果来说,我们算是失败了,但是思路是没有问题的,出问题的地方就在requests模块不能用这个点上,也就是说只要我们能找到一个代替requests模块并且支持异步大的新模块或者方法 ,就可以完美解决这个问题,那么有没有呢?

有!而且也很简单,但是今天先不介绍这一种方法,不要忘了我们今天的目标,进一步理解异步,还有下载美女 美丽的图片!

step4:线程池

关于对异步的进一步理解,如果还是没有听懂也没有关系,因为这条道路任重而道远,小泽还会继续跟大家详细地讲解异步协程这一块的知识!

今天,我们就来用一个异步的下位方法,也就是在一定程度上能代替异步的方法:线程池!

通俗点讲,就是一个人看50部小电影会受不了,但是50个人每人看1部小电影就会意犹未尽,而且也能完成看50部小电影的目标。

废话不多说,直接上高速!

步骤

首先,先导入线程池的模块:

from multiprocessing.dummy import Pool

然后让我们实例化一个线程池的对象!

# 实例化一个有x个线程的线程池对象,这里我们开启两个线程
pool = Pool(2)

准备工作已经完成,接下来我们只需要把任务列表添加到这个线程池里就可以啦,注意,是任务列表哦!

所以我们需要提前准备好一个存放任务的列表:

# 创建一个任务列表
renwu_list = []

接下来,就让我们去找任务吧!

爬取美女 美丽的图片:

首先打开我们的传送门①号,在一开始就有哦,不要偷懒,上去翻一翻!

往下翻一翻,发现图片是会一直出现的,也就是说是阿贾克斯请求,是动态加载的图片!

不管3×7=21,直接打开抓包工具康一康:

用我们猛男的金手指定位到第一张图片,发现了一张图片链接,不过只是个缩略图而已,我们要的上它上面的那个链接,点进去看一看!

看到开头的a了吗,在html网页的编写里,通常用a标签写可以跳转到其他页面的东西,记住喽!

我们一般点的按钮上面,说不定也是一个按钮上面覆盖一个a标签。

点进去后发现有一张比较大的图片了,打开抓包工具康一康够不够大:

可以看到,虽然确实大了一点,但还是不够大,远远满足不了我们!

观察一下图片周围的页面:

发现有两个下载的按钮,那么这两个下载的按钮一定就是向服务器发送请求然后获取更大的图片吧!

但是这里就牵扯到模拟登陆的知识点了,这一块我们后续再讲,所以先爬去中等份的图片,留一点悬念~

为了以防万一,我们还是在抓包工具中全局搜索一下,看看会不会有漏网之鱼!

找完14张jpg格式的图片发现都不是我们想要的大图片,果然是通过请求获得的。

(`・ω・´)

不过没关系,这里我们先观察刚进去时候的页面,就是那个动态加载的页面,找到规律!

一直往下滑会发现,一直有index_xx.html的文件出现,每一份里面有一定量的图片,当然也保存了图片对应的缩略图和跳转页面的url对吧,那我们直接向index_xx.html发送请求,是不是就可以拿到发送过来的页面数据呢,让我们试一试吧:

通过观察,我们发现这些图片都是在ul下的li列表里,所以我们先爬取ul列表下所有的li标签里对应的数据!

import requests
from lxml import etree
url = 'https://www.52dmtp.com/pcwallpaper/index_15.html'
headers = { 
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
# 发起请求获得数据
response = requests.get(url=url,headers=headers).text
# 树化处理返回的数据
tree = etree.HTML(response)
# 定位查找li列表
li_list = tree.xpath('//div[@class="w1200 r content"]/ul/li')
print(li_list)

最后我们打印出li_list,看看有没有数据!

好的,很成功,一个好的开始往往决定了美好的未来,让我们继续前进!

注意我们这里选的url,是index_xx.html,也就是说xx可以是我们自己决定的,因为它每一次返回的数量是固定的,后期可以用循环反复请求!不要纠结不要纠结不要纠结!!!

然后用循环把所有的数据都遍历出来:

# 循环读取列表中每一条li
for li in li_list:
	# 在li中找src,这里我们直接找的缩略图,省事
    data = li.xpath('./div/a/img/@src')[0]

为什么要直接找缩略图呢?因为我们已经得不到完美的图片了,不如不要,所以这里只是为了明确思路,好继续下一步,我们就勉为其难地下载缩略图吧!

# 循环读取列表中每一条li
for li in li_list:
    data = li.xpath('./div/a/img/@src')[0]
    name = li.xpath('./div/a/img/@alt')[0]
    a = requests.get(url=data,headers=headers).content
    with open(name,'wb') as fp:
        fp.write(a)

这里我们把图片的名字也给进行了定位,下载起名一气呵成,运行走你!

好家伙,直接报错,原因大概就是解码的问题吧,不过不要紧,遇到乱码,我们就……

# 循环读取列表中每一条li
for li in li_list:
    data = li.xpath('./div/a/img/@src')[0]
    name = li.xpath('./div/a/img/@alt')[0]
    # 遇到乱码就用这个转换
    name = name.encode('iso-8859-1').decode('utf-8')
    # 由于有的名字太长了,所以弄短一点
    name = name[0:10]+'.jpg'
    a = requests.get(url=data,headers=headers).content
    with open(name,'wb') as fp:
        fp.write(a)

可以看到我们是成功的爬下来的图片,虽然没有爬下来高清大图,但是毕竟还没有学到模拟登陆,所以君子报仇,四年不晚,等我们学会了模拟登陆再去一探究竟!

然后把整个方式封装成一个函数!!

当然url和headers就不用封装进去了,我们可以通过外部输入url来实现动态下载,headers也是要写死的。

呐呐呐,就是这样子,然后我们改一下参数!

然后我们只要往这个方法里放url,就可以了对吧,然后找回我们之前写的线程池的任务列表,把url放进去!

这里小泽就放了两个任务列表,如果你想一次性下很多当然也可以放很多任务进去~

由于线程池部分上面讲过,所以不多累赘,最好是弄个计时,可以看花了多少时间~

相信聪明的你一定能看懂吧!

(`・ω・´)

运行走你:

报错了…

记得这里改一下,hh

再次运行,走你!

可以看到我们就用了0.6秒哈~

如果不用线程池呢? 我们来试一下!

可以看到,用了1.3秒!!

怎么样,厉害吧!!

不过也是下了一堆没用的图片…

到了这里相信大家应该都明白怎么初步提高效率了! 等下次我们再来详细讲解怎么用异步协程来进行这一操作!!

那模拟登陆就到下下次喽~

(`・ω・´)

看评论

写着写着也写到第10篇了,有很多支持小泽的兄弟姐妹们,在这里表示十分感激哈!!!

如果写这些能对你有帮助,那就太好了!!!

不过也有很多小伙伴会有各种各样的需求,有一些是想学这个找工作,还有一些是学着玩,还有的呢就是喜欢小泽(会有吗??)……

咳咳,话不多说,这里尝试给回复一下平时都没有怎么看的评论哈:

不然嘞,你爬一次人家的服务器就要理你一次,刚开始大家都有兴趣,处于摸索阶段,往后就腻了嘛~

这里当然还是希望你能学会而不是来看段子的,hh

为什么你们上班都能摸鱼的…好了不说了,老板来了。

嘿兄弟,犯法的事咱们可不干,不要有心理负担,hh

新版本不一定好用,懂了吧

看大家的反映喽~

这里还是推荐大家有目标的去学,而不是看见哪个方便就学哪个,有时候钻研一个模块也能弄出很厉害的东西的。

嘛,如果你们愿意,我也不介意~

实不相瞒,每次理发那人都说我发质好!

我是怀了吗…还流产呢,好好说话

虽然不知道哈尔是谁…但还是替你高兴吧

那可不

???

学会了都是自己的功劳嘛~所以犒劳一下努力的自己吧!

模拟登陆下下次做哦,不会咕咕咕的那种。

……我也爱你好了

好了,各位也早点睡,hh

(`・ω・´)

本文地址:https://blog.csdn.net/BcXbHello/article/details/110825453