您的位置:首页 > 编程语言 > Python开发

Python 初识多进程概念 —— Queue 进程通信(斗图啦)。

2020-04-27 07:33 369 查看

初学进程通信概念,以斗图啦为例加深印象。

基本思路

构建一个Producer类获取每张图片的地址

class Producer(threading.Thread):
def __init__(self,page_queue,image_queue,image_name,*args,**kwargs):
#page_queue:存放图片页数的一个队列;image_queue:存放图片地址的一个队列;image_name:存放每张图片的一个队列.
super(Producer,self).__init__(*args,**kwargs)
# super 我暂时将其理解为 是调用Producer所继承父类的一个方法,之后将类Producer的对象转换为类threading.Thread的一个对象
self.page_queue = page_queue
self.img_queue = image_queue
self.img_name = image_name
self.headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
def run(self):
#run()方法,与start()都是启动进程的一个方法,但是start()可以启动多个进程,而run()方法不是启动一个新的线程,可以理解为只是主线程中一个执行何种操作的一个入口。
while True:
if self.page_queue.empty():
break
# 当每页都提取完数据后,也就是说存放页数的队列为空的时候,结束Producer
url = self.page_queue.get()
self.parse_page(url)

def parse_page(self,url):
try:
request = urllib.request.Request(url, headers=self.headers)
html = urllib.request.urlopen(request).read()
data = etree.HTML(html)
image_url = data.xpath("//img//@data-original")
image_name = data.xpath("//p[@style = 'display: none']/text()")
# 分别筛选出图片的地址以及名字
for i in range(len(image_url)):
self.img_queue.put(image_url[i])
self.img_name.put(image_name[i])
except:
pass

构建一个Consumer类下载每张图片

class Consumer(threading.Thread):
def __init__(self,page_queue,image_queue,image_name,*args,**kwargs):
super(Consumer,self).__init__(*args,**kwargs)
self.page_queue = page_queue
self.image_queue = image_queue
self.img_name = image_name

def run(self):
while True:
response = requests.get(self.image_queue.get())
if self.image_queue.empty():
break
with open("D:/斗图/"+self.img_name.get()+".jpg","wb") as f:
f.write(response.content)

完整程序

from lxml import etree
import urllib.request
import requests
import threading
from queue import Queue
import os

class Producer(threading.Thread):
def __init__(self,page_queue,image_queue,image_name,*args,**kwargs):
super(Producer,self).__init__(*args,**kwargs)
self.page_queue = page_queue
self.img_queue = image_queue
self.img_name = image_name
self.headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
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):
try:
request = urllib.request.Request(url, headers=self.headers)
html = urllib.request.urlopen(request).read()
data = etree.HTML(html)
image_url = data.xpath("//img//@data-original")
image_name = data.xpath("//p[@style = 'display: none']/text()")
for i in range(len(image_url)):
self.img_queue.put(image_url[i])
self.img_name.put(image_name[i])
except:
pass

class Consumer(threading.Thread):
def __init__(self,page_queue,image_queue,image_name,*args,**kwargs):
super(Consumer,self).__init__(*args,**kwargs)
self.page_queue = page_queue
self.image_queue = image_queue
self.img_name = image_name
def run(self):
while True:
response = requests.get(self.image_queue.get())
if self.image_queue.empty():
break
with open("D:/斗图/"+self.img_name.get()+".jpg","wb") as f:
f.write(response.content)

if __name__=="__main__":
page_queue = Queue(100) # 存放页数,队列容量不需要那么大
image_queue = Queue(500) #
image_name = Queue(500)
s1 = eval(input("爬几页的图?"))
if not os.path.exists("D:/斗图/"):
os.mkdir("D:/斗图/")
# os.mkdir 创建一个相对路径下的文件夹
# 如果要在一个自己指定的未知创建文件夹,只需将os.mkdir() 替换为 os.makedirs()即可
for x in range(1,s1):
url = 'http://www.doutula.com/photo/list/?page=%d'%x
page_queue.put(url)
for x in range(5):
t = Producer(page_queue,image_queue,image_name)
t.start()
for x in range(5):
t = Consumer(page_queue,image_queue,image_name)
t.start()

遇到的未解决问题

  • [Errno 2] No such file or directory: ‘D:/斗图/牛///.jpg’
  • [Errno 2] No such file or directory: ‘D:/斗图/嘿嘿/IN.jpg’
    诸如此类的问题,需要在命名每一张图片时将 名字中含有类似 x/x.jpg 之间的若干个 / 删除,用到replace(’/’,’’),但是在1000张图片中只有很少量个此类的情况,如果每次都需要判断名字中是否含有 / 将增长下载时间。
  • image_queue = Queue(500)
    image_name = Queue(500)
    在程序中规定了图片以及图片名字的容量为500,程序我运行了好多次进行比较,大概就是:
    如果设一次性下载图片的数量在500之内,用的时间 t_1
    设一次性下载图片的数量为1000,用的时间为 t_2
    在测试中 t_2 大于 t_1 ,查的资料后,除与进程数有关外,还与队列的大小有关,如果将:
    image_queue = Queue(500)
    image_name = Queue(500)
    调整为:
    image_queue = Queue(1000)
    image_name = Queue(1000)
    则需要的时间 t_3 将小于 t_2。当数据的数量大于队列的容量时,超出容量的数据只能等待队列有空时,才能继续入列。除了增大队列容量外我还没有具体的方法。
  • 点赞
  • 收藏
  • 分享
  • 文章举报
smart_num_1 发布了2 篇原创文章 · 获赞 8 · 访问量 1494 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: