混迹网络,表情包必不可少,从表情包图片的出现,无疑是席卷网络聊天的态势,涌现了不少网络神图,同时也培养了不少斗图狂魔,今天的沙雕图片你收藏了么?
作为py大(渣)婶(渣),沙雕图片怎么能够缺少呢?网络上有不少表情包图片网站,so,python走起来,搞起!
目标网站:doutula.com 斗图啦
爬取效果:
多线程爬取效果:
几个关键点:
1.图片名规范
由于我们下载图片是以网页上的alt属性命名,在存储为文件名的时候,需要对文本进行规范,某些特殊字符是不能作为图片名存储的!
使用 re.sub函数 把不符合规范的字符 替换
import re imgname=re.sub(r'[\|\/\<\>\:\*\?\\\"]','_',imgname)
2.图片名后缀的获取
图片名后缀,比如gif,jpg,这里可以导入os库的 os.path.splitext 直接切分网页图片地址!
import os suffix = os.path.splitext(img_url)[1] #获取后缀名
3.关于图片的下载,request.urlretrieve函数
注意,需要添加协议头!
from urllib import request opener =request.build_opener() opener.addheaders = [('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1941.0 Safari/537.36')] request.install_opener(opener) request.urlretrieve(img_url, '%s%s'%('doutula/',img_name))
使用request.urlretrieve函数还存在这样一个问题,那就是网络延迟,会容易卡死,这里搜索了一个解决方法,案例参考!
如果有更好的方法不妨留言交流!
#下载文件出现urllib.ContentTooShortError且重新下载文件会存在用时过长的问题,而且往往会尝试好几次,甚至十几次,偶尔会陷入死循环,这种情况是非常不理想的。 #为此,笔者利用socket模块,使得每次重新下载的时间变短,且避免陷入死循环,从而提高运行效率。 import socket import urllib.request #设置超时时间为30s socket.setdefaulttimeout(30) #解决下载不完全问题且避免陷入死循环 try: urllib.request.urlretrieve(url,image_name) except socket.timeout: count = 1 while count <= 5: try: urllib.request.urlretrieve(url,image_name) break except socket.timeout: err_info = 'Reloading for %d time'%count if count == 1 else 'Reloading for %d times'%count print(err_info) count += 1 if count > 5: print("downloading picture fialed!") #来源:CSDN博主「山阴少年」
4.关于图片路径的获取
使用etree解析网页的方式,注意图片路径的获取,这里使用了img[@class!="gif"]排除了多余图片的干扰!
imgs=html.xpath('//div[@class="page-content text-center"]/div//a/img[@class!="gif"]')
5.queue库的使用
queue队列的使用,控制数据的进出,避免多线程错乱!
加入队列:queue.put()
取出队列:queue.get()
获取队列消息个数:queue.qsize()
提示:如果队列满了,那么使用put放入数据会等待,直到队列有空闲位置才可以放入 放入消息的时候不会进行等待,如果发现队列满了不能放入数据,那么会直接崩溃
import multiprocessing import time if __name__ == '__main__': # 创建消息队列 # 3:表示消息队列的最大个数 queue = multiprocessing.Queue(3) # 存放数据 queue.put(1) queue.put("hello") queue.put([1, 5, 8]) # 总结: 队列可以放入任意类型的数据 # 提示: 如果队列满了,那么使用put放入数据会等待,直到队列有空闲位置才可以放入 # queue.put(("xxx", "yyy")) # 放入消息的时候不会进行等待,如果发现队列满了不能放入数据,那么会直接崩溃 # 建议: 放入数据统一put方法 # queue.put_nowait(("xxx", "yyy")) # 判断队列是否满了 result = queue.full() print("队列是否满了:", result) # 使用empty判断队列是否为空,不靠谱 # 解决办法: time.sleep(0.01) # 解决办法: 可以通过qsize判断,但是mac版本没有qsize result = queue.empty() print("队列是否为空:", result) # 获取队列消息的个数 size = queue.qsize() print("消息个数:", size) # 获取队列中数据 result = queue.get() print(result) # 获取队列消息的个数 size = queue.qsize() print("消息个数:", size) # 获取队列中数据 result = queue.get() print(result) # 获取队列消息的个数 size = queue.qsize() print("消息个数:", size) # 获取队列中数据 result = queue.get() print(result) # 获取队列消息的个数 size = queue.qsize() print("消息个数:", size) # 如果队列空了,那么使用get方法会等待队列有消息以后再取值 # result = queue.get() # print(result) # 取值的时候不等待,那么如果发现队列为空,取值不成功那么会直接崩溃 # 建议: 获取数据使用统一get方法 # result = queue.get_nowait() # print(result) #来源:CSDN博主「巨基呀。」
6.最关键的一点,也就是多线程的使用,python多线程生存者消费模型
import threading,time import Queue import random q=Queue.Queue() def Chan(name): for i in range(5): q.put(i) print '%s shengchan %s baozi' %(name,i) time.sleep(random.randrange(5)) def Chi(name): count=0 while count < 5: d=q.get() print '%s chichichi %s baozi' %(name,d) count +=1 time.sleep(random.randrange(5)) p=threading.Thread(target=Chan,args=('AAAAAAAAAAA',)) c=threading.Thread(target=Chi,args=('BBBBBBBBBBB',)) p.start() #来源:CSDN博主「dyeee」
附上单线程代码:
#doutula.com采集 #20191217 by 微信 huguo002 import requests from lxml import etree from urllib import request import os from fake_useragent import UserAgent import re import time def ua(): ua=UserAgent() headers={'User-Agent':ua.random} return headers def parse(url): opener =request.build_opener() opener.addheaders = [('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1941.0 Safari/537.36')] request.install_opener(opener) response=requests.get(url,headers=ua(),timeout=10).content.decode('utf-8') time.sleep(1) html=etree.HTML(response) imgs=html.xpath('//div[@class="page-content text-center"]/div//a/img[@class!="gif"]') """imgs = html.xpath('//div[@class="page-content text-center"]/div//a/img/@data-original') print(imgs) imgnames=html.xpath('//div[@class="page-content text-center"]/div//a/img/@alt') print(imgnames) for img_url,imgname in zip(imgs,imgnames): print(img_url,'%s%s'%(imgname,os.path.splitext(img_url)[1])) r=requests.get(img_url,headers=ua()) with open('doutula/'+'%s%s'%(imgname,os.path.splitext(img_url)[1]),'wb') as f: f.write(r.content) print("图片下载完成!")""" for img in imgs: img_url=img.get('data-original') imgname=img.get('alt') imgname=re.sub(r'[\|\/\<\>\:\*\?\\\"]','_',imgname) suffix = os.path.splitext(img_url)[1] #获取后缀名 img_name='%s%s'%(imgname,suffix) print(img_url) print(img_name) try: request.urlretrieve(img_url, '%s%s'%('doutula/',img_name)) print("图片下载完成!") except: pass def main(): for i in range(1,101): url="http://www.doutula.com/photo/list/?page=%d"% i print(url) parse(url) if __name__=='__main__': main()
附上多线程源码:
#doutula.com采集 #20191217 by 微信 huguo00289 import requests from lxml import etree from urllib import request import os from fake_useragent import UserAgent import re import time import threading from queue import Queue #生产者 class Procuder(threading.Thread): def __init__(self,page_queue,img_queue,*args,**kwargs): super(Procuder,self).__init__(*args,**kwargs) self.page_queue=page_queue self.img_queue=img_queue def run(self): while True: if self.page_queue.empty(): break url=self.page_queue.get() self.parse_page(url) def parse_page(self,url): ua = UserAgent() headers = {'User-Agent': ua.random} response=requests.get(url,headers=headers,timeout=10).content.decode('utf-8') time.sleep(1) html=etree.HTML(response) imgs=html.xpath('//div[@class="page-content text-center"]/div//a/img[@class!="gif"]') for img in imgs: img_url=img.get('data-original') imgname=img.get('alt') imgname=re.sub(r'[\|\/\<\>\:\*\?\\\"]','_',imgname) suffix = os.path.splitext(img_url)[1] #获取后缀名 img_name='%s%s'%(imgname,suffix) self.img_queue.put((img_url,img_name)) #消费者 class Consumer(threading.Thread): def __init__(self,page_queue,img_queue,*args,**kwargs): super(Consumer,self).__init__(*args,**kwargs) self.page_queue=page_queue self.img_queue=img_queue def run(self): opener = request.build_opener() opener.addheaders = [('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1941.0 Safari/537.36')] request.install_opener(opener) while True: if self.img_queue.empty() and self.page_queue.empty(): break img_url,img_name=self.img_queue.get() try: request.urlretrieve(img_url, '%s%s' % ('doutula2/', img_name)) print("图片下载完成!") except: pass def main(): page_queue=Queue(100) img_queue=Queue(10000) for i in range(1,101): url="http://www.doutula.com/photo/list/?page=%d"% i print(url) page_queue.put(url) for x in range(8): t=Procuder(page_queue,img_queue) t.start() for x in range(5): t=Consumer(page_queue,img_queue) t.start() if __name__=='__main__': main()
多线程版本使用了while循环,同时使用了if判断,break退出循环,不过存在问题,当翻页数过少,比如3的还是会跳过图片下载!
代码参考来源:
知了课堂-黄勇
零基础:21天搞定Python分布爬虫
https://study.163.com/course/introduction/1004530011.htm
课时75:【多线程】实战-下载表情包之同步爬虫完成
课时76:【多线程】实战-下载表情包之异步爬虫完成
感兴趣可以私聊我观看视频内容!