python queue队列实战

概念:Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,用来在生产者和消费者线程之间的信息传递Python多线程主要是为了提高程序在IO方面的优势,在爬虫的过程中显得尤为重要。正常的爬虫请求直接封装多线程,但是爬虫请求的过程中,对于url的请求需要通过队列来实现。

queue实战生产者消费者

1、首先先导入Queue、threading

from queue import Queue
import threading

2、定义生产者生产(生产初始url)

# 定义生产者
class BiAnProducer(threading.Thread):
    def __init__(self, url_queue, respone_queue):
        # 初始化父类
        threading.Thread.__init__(self)
        # 初始化url队列
        self.url_queue = url_queue
        # 初始化响应数据队列
        self.respone_queue = respone_queue
 
    # 运行线程的方法 run
    def run(self) -> None:
        # 死循环一直把队列的数据取完为止
        while True:
            # 判断队列是否为空 为空的话跳出循环
            if self.url_queue.empty():
                break
            try:
                url = self.url_queue.get()
                response = requests.get(url=url, headers=headers)
                response.encoding = response.apparent_encoding
                self.respone_queue.put(response.text)
            except:
                pass

3、定义消费者(解析数据)

# 定义消费者
class BiAnConsumer(threading.Thread):
    def __init__(self,respone_queue, img_url_queue, img_title_queue):
        threading.Thread.__init__(self)
        self.respone_queue = respone_queue
        self.img_url_queue = img_url_queue
        self.img_title_queue = img_title_queue
 
    def run(self) -> None:
        # 死循环一直把队列的数据取完为止
        while True:
            # 判断队列是否为空 为空的话跳出循环
            if self.respone_queue.empty():
                break
            try:
                # 从队列中取出响应数据
                response = self.respone_queue.get()
                # 调用解析数据方法
                self.parse_data(response)
            except:
                pass
    
    # 解析数据方法
    def parse_data(self, response):
        tree = etree.HTML(response)
        title = tree.xpath('//*[@id="main"]/div[3]/ul/li/a/img/@alt')
        img_url = tree.xpath('//*[@id="main"]/div[3]/ul/li/a/img/@src')
        for titles, img_urls in zip(title, img_url):
            p_img_url = 'https://pic.netbian.com' + img_urls
            self.img_url_queue.put(p_img_url)
            self.img_title_queue.put(titles)

4、在定义一个消费者(下载数据)

# 再来一个消费者
class BiAnDownload(threading.Thread):
    def __init__(self, img_url_queue, img_title_queue):
        threading.Thread.__init__(self)
        # 初始化图片url队列
        self.img_url_queue = img_url_queue
        # 初始化标题队列
        self.img_title_queue = img_title_queue
 
    def run(self) -> None:
        while True:
            if self.img_url_queue.empty() and self.img_title_queue.empty():
                break
            try:
                # 从队列中取出图片url
                img_url = self.img_url_queue.get()
                # 从队列中取出图片标题
                title = self.img_title_queue.get()
                content = requests.get(url=img_url, headers=headers).content
                # 调用下载方法
                self.download(content, title)
            except:
                pass
 
    # 下载方法            
    def download(self, content, title):
        f_path = path + '/' + title + '.jpg'
        try:
            with open(f_path, 'wb') as f:
                f.write(content)
                f.close
        except Exception as e:
            print('ERROR', e)

5、主方法

if __name__ =="__main__":
    start = time.time()
    # 创建初始url队列
    url_queue = Queue()
    # 创建容纳响应数据的队列
    response_queue = Queue()
    # 创建需要下载的url队列
    img_url_queue = Queue()
    # 创建标题队列
    img_title_queue = Queue()
 
    # 控制url个数
    url = 'https://pic.netbian.com/index_{}.html'
    for page in range(1, 11):
        new_url = url.format(page)
        url_queue.put(new_url)
 
    # 创建一个列表去控制爬虫线程
    crawl_list = []
    for i in range(0, 5):
        crawl = BiAnProducer(url_queue, response_queue)
        crawl_list.append(crawl)
        crawl.start()   # 启动
        
    for i in crawl_list:
        i.join()    # join就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程在终止
 
    # 创建一个列表去控制解析线程
    parse_list = []
    for i in range(0, 5):
        parse = BiAnConsumer(response_queue, img_url_queue, img_title_queue)
        parse_list.append(parse)
        parse.start()
        
    for i in parse_list:
        i.join()
 
    # 创建一个列表去控制下载线程
    down_list = []
    # 开启五个线程
    for i in range(0, 5):
        download = BiAnDownload(img_url_queue, img_title_queue)
        down_list.append(download)
        download.start()
 
    for i in down_list:
        i.join()
    
    end = time.time()
    print('总耗时:', end - start)

6、完整代码

 
import requests
from lxml import etree
from fake_useragent import UserAgent
import threading
from queue import Queue
import os
import time
 
# 创建文件夹
path = r'D:\壁纸\高清无码'
isExists=os.path.exists(path)
if not isExists:
    os.mkdir(path)
 
# 请求头
headers = {'User-Agent': UserAgent().random}
 
# 定义生产者
class BiAnProducer(threading.Thread):
    def __init__(self, url_queue, respone_queue):
        # 初始化父类
        threading.Thread.__init__(self)
        # 初始化url队列
        self.url_queue = url_queue
        # 初始化响应数据队列
        self.respone_queue = respone_queue
 
    # 运行线程的方法 run
    def run(self) -> None:
        # 死循环一直把队列的数据取完为止
        while True:
            # 判断队列是否为空 为空的话跳出循环
            if self.url_queue.empty():
                break
            try:
                url = self.url_queue.get()
                response = requests.get(url=url, headers=headers)
                response.encoding = response.apparent_encoding
                self.respone_queue.put(response.text)
            except:
                pass
 
# 定义消费者
class BiAnConsumer(threading.Thread):
    def __init__(self,respone_queue, img_url_queue, img_title_queue):
        threading.Thread.__init__(self)
        self.respone_queue = respone_queue
        self.img_url_queue = img_url_queue
        self.img_title_queue = img_title_queue
 
    def run(self) -> None:
        # 死循环一直把队列的数据取完为止
        while True:
            # 判断队列是否为空 为空的话跳出循环
            if self.respone_queue.empty():
                break
            try:
                # 从队列中取出响应数据
                response = self.respone_queue.get()
                # 调用解析数据方法
                self.parse_data(response)
            except:
                pass
    
    # 解析数据方法
    def parse_data(self, response):
        tree = etree.HTML(response)
        title = tree.xpath('//*[@id="main"]/div[3]/ul/li/a/img/@alt')
        img_url = tree.xpath('//*[@id="main"]/div[3]/ul/li/a/img/@src')
        for titles, img_urls in zip(title, img_url):
            p_img_url = 'https://pic.netbian.com' + img_urls
            self.img_url_queue.put(p_img_url)
            self.img_title_queue.put(titles)
 
# 再来一个消费者
class BiAnDownload(threading.Thread):
    def __init__(self, img_url_queue, img_title_queue):
        threading.Thread.__init__(self)
        # 初始化图片url队列
        self.img_url_queue = img_url_queue
        # 初始化标题队列
        self.img_title_queue = img_title_queue
 
    def run(self) -> None:
        while True:
            if self.img_url_queue.empty() and self.img_title_queue.empty():
                break
            try:
                # 从队列中取出图片url
                img_url = self.img_url_queue.get()
                # 从队列中取出图片标题
                title = self.img_title_queue.get()
                content = requests.get(url=img_url, headers=headers).content
                # 调用下载方法
                self.download(content, title)
            except:
                pass
 
    # 下载方法            
    def download(self, content, title):
        f_path = path + '/' + title + '.jpg'
        try:
            with open(f_path, 'wb') as f:
                f.write(content)
                f.close
        except Exception as e:
            print('ERROR', e)
 
        
if __name__ =="__main__":
    start = time.time()
    # 创建初始url队列
    url_queue = Queue()
    # 创建容纳响应数据的队列
    response_queue = Queue()
    # 创建需要下载的url队列
    img_url_queue = Queue()
    # 创建标题队列
    img_title_queue = Queue()
 
    # 控制url个数
    url = 'https://pic.netbian.com/index_{}.html'
    for page in range(1, 11):
        new_url = url.format(page)
        url_queue.put(new_url)
 
    # 创建一个列表去控制爬虫线程
    crawl_list = []
    for i in range(0, 5):
        crawl = BiAnProducer(url_queue, response_queue)
        crawl_list.append(crawl)
        crawl.start()   # 启动
        
    for i in crawl_list:
        i.join()    # join就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程在终止
 
    # 创建一个列表去控制解析线程
    parse_list = []
    for i in range(0, 5):
        parse = BiAnConsumer(response_queue, img_url_queue, img_title_queue)
        parse_list.append(parse)
        parse.start()
        
    for i in parse_list:
        i.join()
 
    # 创建一个列表去控制下载线程
    down_list = []
    # 开启五个线程
    for i in range(0, 5):
        download = BiAnDownload(img_url_queue, img_title_queue)
        down_list.append(download)
        download.start()
 
    for i in down_list:
        i.join()
    
    end = time.time()
    print('总耗时:', end - start)

为了避免中招 我只下载了10页数据一共花了7秒6(我的网速是比较慢的)

 

  下载下来的一部分数据

 

posted @ 2022-08-20 22:43  阿里多多酱  阅读(152)  评论(0编辑  收藏  举报