python多线程爬虫之多线程之间的通信——队列之生产与消费模式

1 篇文章 0 订阅
0 篇文章 0 订阅

在上上篇文章讲多线程的时候附上了一则爬取腾讯招聘的多线程爬虫,也用到了队列,刚刚我做了写改动,就再写一篇文章把代码贴出来吧:

class Tencent(Thread):     #生产者线程,专门发送请求,然后将请求结果放入队列
    
    def __init__(self,queue,t):
        self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36'}
        self.queue = queue   #传入任务队列
        self.t = t
        super(Tencent, self).__init__()

    def run(self):
        while True:
            if self.queue.empty():   #如果任务队列为空,则结束循环
                break
            print(f'这是{self.t}线程')
            try:
                q = self.queue.get(block=False)  #当从队列中无法取到值,则报错,不会阻塞,这是为了防止多线程中有漏网之鱼,比如两个线程几乎同时判断队列中还有一个任务,于是二人都通过了验证
                url = 'https://careers.tencent.com/tencentcareer/api/post/Query?keyword=python&pageIndex=%s&pageSize=10&language=zh-cn&area=cn'%q
                response = self.get_response(url)
                response_queue.put(response)
            except Exception as e:
                pass
            print(f'{self.t}线程结束了')

    def get_response(self,url):    #请求的方法
        response = requests.get(url=url, headers=self.headers)
        response = response.content.decode()
        return response






class GetTencent(Thread):  #消费者线程,从队列中获取请求,然后进行数据处理并存入文件
    def __init__(self,t2,lock): 
        self.t2 = t2
        self.lock = lock  #加了把锁,用于存入文件时使用,防止数据存储混乱
        super().__init__()

    def run(self):
        while True:
            if not response_queue.empty(): #判断是否队列中还有请求,有则进行提取请求
                print(f'这是{self.t2}线程')
                try:
                    response = response_queue.get(block=False) #与上方做法同理
                    self.load_str(response)
                except Exception as e:
                    pass
                print(f'{self.t2}线程结束了')
            elif response_queue.empty() and not status: #判断是否生产的线程全部死亡,并且队列内无请求对象了,如果是,则结束循环,结束消费
                break
    def load_str(self, response):  #数据处理的方法
        response = response.replace(r'\n', '')
        response = response.replace(r'\r', '')
        response = json.loads(response)
        data_list = response['Data']['Posts']
        for data in data_list:
            RecruitPostName = data['RecruitPostName']
            CountryName = data['CountryName']
            LocationName = data['LocationName']
            Responsibility = data['Responsibility']
            LastUpdateTime = data['LastUpdateTime']
            with self.lock:
                with open('tencent.txt', 'a', encoding='utf-8') as f:
                    f.write(
                        RecruitPostName + ',' + CountryName + ',' + LocationName + ',' + Responsibility + ',' + LastUpdateTime + '\n')


response_queue = Queue()  #申明一个全局的队列用于存放请求
status = True  #实现申明一个生产的线程的状态,如果结束,则调为False
if __name__ == '__main__':
    q = Queue()  #往队列中放任务,让生产者线程去取
    for i in range(1,11):
        q.put(i)

    t_list = ['q1','q2','q3']
    t2_list = ['c1','c2','c3']
    list1 = []
    list2 = []
    start_time = time.time()
    print(f'开始时间{start_time}')
    for t in t_list:  #启动三个生产者线程
        tencent = Tencent(q,t)
        tencent.start()
        list1.append(tencent)

    lock = Lock()  #实例化一把锁
    for t2 in t2_list:  #启用三个消费者线程
        get_tencent = GetTencent(t2,lock)
        get_tencent.start()
        list2.append(get_tencent)



    for j in list1:  #用主线程检测是否生产者线程全部死亡
        j.join()
    status = False  #执行到这一步证明生产者线程都死亡了,改变状态
    for j in list2:  #主线程阻塞,直到所有请求都被消费者线程处理完毕
        j.join()
    print(f'耗费时间:{time.time()-start_time}')  #打印总耗时

看过我之前那篇文章的朋友应该有疑问,为什么要改成这样写,没有为什么,只为了方便你加深队列与生产消费关系的理解
ok,看下打印结果

开始时间1567078647.517759
这是q1线程
这是q2线程
这是q3线程
q3线程结束了这是c3线程
这是c2线程
这是c1线程

c3线程结束了
c1线程结束了
这是q3线程
q2线程结束了
这是q2线程
这是c3线程
q1线程结束了
这是q1线程
这是c1线程
c3线程结束了
c2线程结束了
c1线程结束了
q3线程结束了这是c2线程这是c3线程


这是q3线程这是c1线程

c2线程结束了c3线程结束了

q2线程结束了
这是c2线程
这是q2线程
q1线程结束了这是c3线程
这是q1线程

c2线程结束了
c1线程结束了
c3线程结束了
q3线程结束了这是c3线程
这是c2线程
这是q3线程这是c1线程


c1线程结束了
c3线程结束了
c2线程结束了
q2线程结束了这是c1线程

这是c3线程这是c2线程

c2线程结束了c3线程结束了

q1线程结束了这是c3线程
这是c2线程

c2线程结束了
q3线程结束了
这是c2线程
c3线程结束了
c1线程结束了
c2线程结束了
耗费时间:1.2140693664550781

嗯嗯,差不多了,就先分享到这里了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值