Python多线程爬虫教你如何快速下载表情包,告别斗图斗不赢的烦恼!

前言

在QQ斗图中,为什么有些人总有斗不完的图,今天,这里有了这个斗图小程序,终于可以告别斗图斗不赢的痛了。

1.完成这个小程序需要导入的模块

urllib,bs4,threading,queue,os,time,random
urllib和bs4这两个模块是第三方库,需要额外安装
我们按win+R 输入cmd ,然后输入 ***pip install urllib3 ,pip install bs4***安装成功即可(注意:这是要在配好环境变量的前提下)

2.了解两个HTTP状态码

这个小程序,在做的过程中,会报服务器相关原因的错误。

状态码 内容 说明
4xx 客服端错误 这类的状态码代表了客服端看起来可能发生了错误,妨碍了服务器的处理
403 Forbidden 服务器已经理解请求,当时拒绝执行它
404 Not Found 请求失败,请求的资源在服务器上找不到

这两个错误待会会在我们实现这个小程序的过程当中逐渐显现出来。

3.怎样实现

我们需要来到这个网址 ***http://www.doutula.com/***,进入这个界面,输入一个表情包的类型
如我输入 龙王在这里插入图片描述

可以发现这个网址好像就是我们能爬取这一页表情包的网址了,但这样的表情包总不可能只有一页,来到第二页,可以发现,网址变了。
在这里插入图片描述
我们要爬取所有页的表情包,就只有根据这个网址来进行爬取。
http://www.doutula.com/search?type=photo&more=1&keyword=%E9%BE%99%E7%8E%8B&page=2
对这个网址进行分析,我们可以发现,其实主要变化的就是keywordpage,keyword是我们输入表情包的类型,page是这类表情包的页码,为了能爬取所有页的表情包,我们只需改变页码的值就行了,当这个网址出现404错误,就表明所有这类表情包已经基本爬取完毕!

代码如下:

def get_url():
    num=1       # 主要通过这个参数得到用户输入类型的所有表情包
    imgs_url=[]   # 用于返回表情包的下载链接
    imgs_name=[]  # 用于返回表情包的名称
    keyword=input('请输入你想下载的表情包类型:')
    keyword_1=parse.urlencode({'keyword':keyword})
    while True:
        url='http://www.doutula.com/search?type=photo&more=1&{}&page={}'.format(keyword_1,str(num))
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3741.400 QQBrowser/10.5.3863.400'
        }  # 请求头
        try:
            request_1=request.Request(url=url,headers=headers)
            html=request.urlopen(request_1)
            html=html.read().decode('utf-8')
            soup=BeautifulSoup(html,'lxml')
            list_1=soup.select('a.col-xs-6.col-md-2')

            for html_1 in list_1:
                try:
                    imgs_url.append(html_1.select('img')[-1]['data-original'])
                    imgs_name.append(html_1.select('p')[0].get_text())
                except:
                    time.sleep(0.125)
        except Exception as e:
            print(e,'所有图片加载完毕!')
            break  # 当出现404时表明此时所有图片已经都得到了

        num+=1

    return imgs_name,imgs_url,keyword

代码中之所以这句代码要以-1为索引,是因为动态图片的网页中有两个 img 标签,且最后面那个 img标签下面有表情包的下载链接,而静态表情包 只有一个 img 标签。

 imgs_url.append(html_1.select('img')[-1]['data-original'])

通过这个函数,我们可以得到表情包的下载链接、表情包的名称。不加一个请求头的话,会出现403错误,如下:
在这里插入图片描述
加上请求头:
在这里插入图片描述
之后,我们就只要多线程下载这些表情包即可。

def Downlad(queue_imgsname:Queue,queue_urls:Queue,keyword:str):
    path_1='./'+keyword
    try:
        os.mkdir(path_1)
    except:
        pass
    while True:
        if queue_imgsname.empty():
            break
        imgname=queue_imgsname.get()
        url_1=queue_urls.get()
        print('线程{}正在下载{}'.format(threading.current_thread().getName(),imgname))
        try:
            request.urlretrieve(url=url_1,filename=path_1+'/'+'-【{}】.gif'.format(random.random()))
            #用随机数来命名文件名称是为了防止出现文件名命名出错的情况 osError,也能避免因同名的表情包被覆盖的情况
        except Exception as e:
            print(e)
            time.sleep(0.125) # 如果文件名有错误,那就休眠0.125秒
        finally:
            time.sleep(0.25)

这是那个实现多线程下载的那个函数,之所以命名图片后缀名为“.gif”是表情包有动态的原因。
还有部分包情包无法下载,会出现403错误。

4.最终代码

import urllib.parse as parse
from urllib import request
from bs4 import BeautifulSoup
import threading
from queue import Queue
import os
import random
import time


def get_url():
    num=1       # 主要通过这个参数得到用户输入类型的所有表情包
    imgs_url=[]   # 用于返回表情包的下载链接
    imgs_name=[]  # 用于返回表情包的名称
    keyword=input('请输入你想下载的表情包类型:')
    keyword_1=parse.urlencode({'keyword':keyword})
    while True:
        url='http://www.doutula.com/search?type=photo&more=1&{}&page={}'.format(keyword_1,str(num))
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3741.400 QQBrowser/10.5.3863.400'
        }  # 请求头
        try:
            request_1=request.Request(url=url,headers=headers)
            html=request.urlopen(request_1)
            html=html.read().decode('utf-8')
            soup=BeautifulSoup(html,'lxml')
            list_1=soup.select('a.col-xs-6.col-md-2')

            for html_1 in list_1:
                try:
                    imgs_url.append(html_1.select('img')[-1]['data-original'])
                    imgs_name.append(html_1.select('p')[0].get_text())
                except:
                    time.sleep(0.125)
        except Exception as e:
            print(e,'所有图片加载完毕!')
            break  # 当出现404时表明此时所有图片已经都得到了

        num+=1

    return imgs_name,imgs_url,keyword

def Downlad(queue_imgsname:Queue,queue_urls:Queue,keyword:str):
    path_1='./'+keyword
    try:
        os.mkdir(path_1)
    except:
        pass
    while True:
        if queue_imgsname.empty():
            break
        imgname=queue_imgsname.get()
        url_1=queue_urls.get()
        print('线程{}正在下载{}'.format(threading.current_thread().getName(),imgname))
        try:
            request.urlretrieve(url=url_1,filename=path_1+'/'+'-【{}】.gif'.format(random.random()))
            #用随机数来命名文件名称是为了防止出现文件名命名出错的情况 osError,也能避免因同名的表情包被覆盖的情况
        except Exception as e:
            print(e)
            time.sleep(0.125) # 如果文件名有错误,那就休眠0.125秒
        finally:
            time.sleep(0.25)


if __name__ == '__main__':
    tuple_1=get_url()
    imgs_name,imgs_url,keyword=tuple_1[0],tuple_1[1],tuple_1[2]
    queue_imgsname=Queue(len(imgs_name)+5)  #防止队列出现阻塞现象
    queue_urls=Queue(len(imgs_url)+5)
    threading_num=len(imgs_name)//10  # 根据表情包的数量来创建相应多的线程
    threading_list=[]  # 线程列表
    for i in range(len(imgs_name)):
        queue_imgsname.put(imgs_name[i])
        queue_urls.put(imgs_url[i])

    time.sleep(5)  # 在开始下载之前,休眠五秒
    print('开始下载!')
    for i in range(threading_num):
        threading_1=threading.Thread(target=Downlad,args=(queue_imgsname,queue_urls,keyword,))
        threading_1.start()
        threading_list.append(threading_1)

    for i in threading_list:
        i.join()

    print('--------------------下载完毕!当前线程为{}'.format(threading.current_thread().getName()))


运行结果:
在这里插入图片描述
这个运行之后,同一个文件夹下面会多一个文件夹,文件夹的名称就是自己输入表情包的类型
在这里插入图片描述
只不过下载的图片中有个别表情包出错了。
下载动态图片试一下:
在这里插入图片描述
点击其中一张,可以看到表情包是动态的。

5.改进与总结

1.本小程序还有一定的改进,如下载的表情包出错,如果大家有什么好的改进措施,欢迎在下方留言;
2.文章表达不清楚的,希望大家谅解,我相信以后我一定写的更好。

发布了51 篇原创文章 · 获赞 299 · 访问量 9174

猜你喜欢

转载自blog.csdn.net/qq_45404396/article/details/104854399
今日推荐