什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
这个阻塞队列就是用来给生产者和消费者解耦的。纵观大多数设计模式,都会找一个第三者出来进行解耦
那么为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
代码实现:
import requests
from lxml import etree
from threading import Thread
from queue import Queue
# 爬取页面内容
def getContent(page_queue,s_name):
headers = {
'User-agent': 'ios/xxxx.3.3.3.10'
}
while True:
# 表示如果任务队列不为空,当前爬虫不休息
if page_queue.empty():
break
# 获取页面内容
page = page_queue.get()
base_url = 'https://hr.tencent.com/position.php?start='
url = base_url + str((page - 1) * 10)
response = requests.get(url, headers=headers)
print('我是%s线程,我正在执行页码%d的页面'%(s_name,page))
# 生产者-----将得到的页面内容放到content队列当中
content_queue.put(response.text)
# 解析页面数据
def parse_content(p_name):
while not exit_flag:
try:
# 消费者--- 从content队列当中获取页面内容,进行解析
content = content_queue.get(block=False)
# print('~~~~~~~~~~~~~我是%s~~~~~~~~'%(p_name))
# 二、解析
tree = etree.HTML(content)
position_info_list = tree.xpath('//tr[@class="even"] | //tr[@class="odd"]')
for position_info in position_info_list:
# 实例化 收集信息
name = position_info.xpath('.//td[1]/a/text()')[0]
# detailLink = 'https://hr.tencent.com/' + position_info.xpath('.//td[1]/a/@href')[0]
# detailLink = detailLink
# position = position_info.xpath('.//td[2]/text()')
# positionInfo = position[0] if position else ''
#
# ipeopleNumber = position_info.xpath('.//td[3]/text()')[0]
# workLocation = position_info.xpath('.//td[4]/text()')[0]
publishTime = position_info.xpath('.//td[5]/text()')[0]
job = name + ',' + publishTime + '\n'
with open('tencent.txt', 'a', encoding='utf-8') as fp:
fp.write(job)
print('~~~~~~~~~~~~~我是%s,我完成了解析页面的任务~~~~~~~~' % (p_name))
except:
print('~~~~~~~~~~没取到,我报异常了~~~~~~~~~~~~~~')
pass
# 定义一个存放页面内容队列 content
content_queue = Queue()
# 解析线程退出的标志
exit_flag = False
if __name__ == '__main__':
# 任务队列
page_queue = Queue()
for page in range(1, 50, 1):
page_queue.put(page)
# 创建三个爬虫线程
spider_name = ['爬虫1','爬虫2','爬虫3']
spider_list = []
for name in spider_name:
# 不是说线程越多越好,频繁地切换线程也需要消耗资源。
t = Thread(target=getContent, args=(page_queue,name)) # 接元祖
t.start()
spider_list.append(t)
# 创建3个解析线程
parse_name = ['解析线程1','解析线程2','解析线程3']
for name in parse_name:
t = Thread(target=parse_content, args=(name,)) # 接元祖
t.start()
# 解析线程退出
# page_queue为空
while not page_queue.empty():
# print('passsssssssssssssssssssssssss')
pass
for spider in spider_list:
spider.join()
print('~~~~~~~~~~~~~~~~所有页面都请求回来了,而且都放在content_queue当中了~~~~~~~~~~~~~~~~~~~~')
exit_flag = True# 可以退出了