前言
在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
对这个网址进行分析,我们可以发现,其实主要变化的就是keyword和page,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.文章表达不清楚的,希望大家谅解,我相信以后我一定写的更好。