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(我的网速是比较慢的)
下载下来的一部分数据