菜菜的并发编程笔记 |(四)Python实战生产者-消费者模式多线程爬虫

在这里插入图片描述
系列索引:菜菜的并发编程笔记 | Python并发编程详解(持续更新~)

一、多组件的Pipeline技术架构

在这里插入图片描述

二、生产者消费者爬虫的架构

在这里插入图片描述
对于没有学过操作系统的同学可以理解成在生产者和消费者之间设置一个缓冲区,生产者生产的数据放入缓冲区,消费者从中取出数据供自己使用。

生产者消费者问题是一个非常典型的问题,我觉得没有学过这一块知识的最好去补习一下,对后面学习理解线程通信、信号量机制都有帮助。

三、多线程数据通信的queue.Queue

在这里插入图片描述
多线程之间通信可以使用queue类,在线程间创建一个队列,这是一种线程安全的方式,当队列为空时消费者会阻塞直到有数据被放入对列,右侧是一些常用的查询方法。

四、代码编写实现生产者-消费者爬虫

基础代码部分参考上一篇文章,在代码中我们创建两个对列,分别是url_queuehtml_queue,函数do_craw可以看做生产者,获取网页数据用来给消费者do_parse提供数据。

两个函数都先从对列中取数据,若对列为空则阻塞执行,以do_craw为例,当从queue中获取到数据后将获取到的数据(url)传入爬虫程序执行,然后将中间数据(即获取到的未解析的网页)放入另一queue对列,然后输出执行结果并随机休眠,防止服务器负载过大崩掉!但大家注意随机休息会使执行时间变长,而不是因为使用queue造成的效率变低!

import requests
from bs4 import BeautifulSoup

urls = [
    f"https://www.cnblogs.com/sitehome/p/{page}"
    for page in range(1, 50 + 1)
]

def craw(url):
    #print("craw url: ", url)
    r = requests.get(url)
    return r.text

def parse(html):
    # class="post-item-title"
    soup = BeautifulSoup(html, "html.parser")
    links = soup.find_all("a", class_="post-item-title")
    return [(link["href"], link.get_text()) for link in links]

if __name__ == "__main__":
    for result in parse(craw(urls[2])):
        print(result)

上面是blog_spider的代码部分。

import queue
import blog_spider
import time
import random
import threading

def do_craw(url_queue: queue.Queue, html_queue: queue.Queue):
    while True:
        url = url_queue.get()
        html = blog_spider.craw(url)
        html_queue.put(html)
        print(threading.current_thread().name, f"craw {url}",
              "url_queue.size=", url_queue.qsize())
        time.sleep(random.randint(1, 2))

def do_parse(html_queue: queue.Queue, fout):
    while True:
        html = html_queue.get()
        results = blog_spider.parse(html)
        for result in results:
            fout.write(str(result) + "\n")
        print(threading.current_thread().name, f"results.size", len(results),
              "html_queue.size=", html_queue.qsize())
        time.sleep(random.randint(1, 2))

if __name__ == "__main__":
    url_queue = queue.Queue()
    html_queue = queue.Queue()
    for url in blog_spider.urls:
        url_queue.put(url)

    for idx in range(3):
        t = threading.Thread(target=do_craw, args=(url_queue, html_queue),
                             name=f"craw{idx}")
        t.start()

    fout = open("02.data.txt", "w")
    for idx in range(2):
        t = threading.Thread(target=do_parse, args=(html_queue, fout),
                             name=f"parse{idx}")
        t.start()

在main函数中主要是创建对列,把urls数据放入对列,然后先执行爬虫线程,再执行解析线程。下一篇我们将具体讲讲并发编写中线程安全问题以及Lock解决方案。

Python进阶之并发编程篇持续更新,欢迎点赞收藏关注

上一篇:菜菜的并发编程笔记 |(三)利用多线程实现爬虫10倍加速
下一篇:菜菜的并发编程笔记 |(五)线程安全问题以及Lock解决方案

本人水平有限,文章中不足之处欢迎下方👇评论区批评指正~

如果感觉对你有帮助,点个赞👍 支持一下吧 ~

不定期分享 有趣、有料、有营养内容,欢迎 订阅关注 🤝 我的博客 ,期待在这与你相遇 ~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿知

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值