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