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

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

构建一个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。当数据的数量大于队列的容量时,超出容量的数据只能等待队列有空时,才能继续入列。除了增大队列容量外我还没有具体的方法。
原创文章 4 获赞 12 访问量 4917

猜你喜欢

转载自blog.csdn.net/smart_num_1/article/details/105537242