混迹网络,表情包必不可少,从表情包图片的出现,无疑是席卷网络聊天的态势,涌现了不少网络神图,同时也培养了不少斗图狂魔,今天的沙雕图片你收藏了么?
作为py大(渣)婶(渣),沙雕图片怎么能够缺少呢?网络上有不少表情包图片网站,so,python走起来,搞起!
目标网站:http://www.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的还是会跳过图片下载!
本文分享自 Python与SEO学习 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!