爬虫扩展——多协程该咋说呢?模板教你学会

本文介绍了协程的基本概念,对比了同步与异步,展示了如何用gevent实现多线程爬虫,并结合queue模块避免过度请求,提高效率。通过实例演示了如何使用队列控制并发任务和处理爬虫负载均衡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一. 协程的概念

 二. 同步与异步

三. 使用gevent实现多协程

四. queue模块

五. 总结

爬虫文章专栏


一. 协程的概念

1)什么是协程?

        协程指的是⼀个CPU下由程序实现多个任务并发进⾏,它是⼀种异步执行方式即把本来由操作系统控制任务切换+保存状态,直接在应用程序⾥实现了;

2)为何用协程?

        把单个线程的io降到最低,最⼤限度地提升单个线程的执行效率;

 3)多协程:

        多协程就是利⽤程序控制多个爬⾍任务⽆须等待的交替执⾏,从⽽省去等待的时间和切
换任务的时间。


 二. 同步与异步

        就以我们运行爬虫代码为例 :

  • 同步任务: 第⼀个网站爬取完了,代码接收到完成信息了,把完成信息通知给第⼆个网站,第
    ⼆个网站接到通知后,开始准备爬取;
  • 异步任务: ⼏个网站同时预备爬取,⼀个完成了就下⼀个上,两两之间不需要通信, 也就是多个协程任务以异步执⾏的⽅式,可以省去等待和切换任务时的时间,使得执⾏得更快了;
        以看电影的例子帮助大家更好的理解这个概念:
        我们使用python运⾏代码时,总是⼀个函数结束之后再进⾏下⼀个函数,⼀个任务等着⼀个任务执行完,才开始,等待的过程和切换任务都是非常耗费时间的,这样⼦我 们要爬⼤量数据时,就太麻烦了。 而我们如果使用gevent,就可以让多个任务交替实现,不用等待,不用切换任务,异步执 行。
         这其实也就是同步和异步的区别。

三. 使用gevent实现多协程

我先把我之前使用过的一个利用gevent实现多线程的代码给大家展示一下:

from gevent import monkey
#从gevent库里导入monkey模块。
monkey.patch_all()
#monkey.patch_all()能把程序变成协作式运行,就是可以帮助程序实现异步。
import gevent,time,requests
#导入gevent、time、requests

start = time.time() 
#记录程序开始时间。
url_list = ['https://www.baidu.com/', 'https://www.sina.com.cn/', 'http://www.sohu.com/', 'https://www.qq.com/', 
            'https://www.163.com/', 'http://www.iqiyi.com/', 'https://www.tmall.com/', 'http://www.ifeng.com/'] 
#把8个网站封装成列表。
def crawler(url):
#定义一个crawler()函数。
    r = requests.get(url)
    #用requests.get()函数爬取网站。
    print(url,time.time()-start,r.status_code)
    #打印网址、请求运行时间、状态码。
    tasks_list = [ ]
    #创建空的任务列表。
    for url in url_list:
    #遍历url_list。
        task = gevent.spawn(crawler,url)
        #用gevent.spawn()函数创建任务。
        tasks_list.append(task)
        #往任务列表添加任务。
    gevent.joinall(tasks_list)
    #执行任务列表里的所有任务,就是让爬虫开始爬取网站。
    end = time.time()
    #记录程序结束时间。
    print(end-start)
    #打印程序最终所需时间

以下我将代码分开给大家讲解以下 

1)插入猴子补丁:

from gevent import monkey 
#从gevent库里导入monkey模块。
monkey.patch_all()
#monkey.patch_all()能把程序变成协作式运行,就是可以帮助程序实现异步。

        猴子补丁:猴子补丁的主要目的是为了给其他模块补充内容,能够变成协作运行的执行方式;(注意:猴子补丁必须放在被打补丁的前面,如time、requests模块之前) 

2)定义⼀个爬虫任务函数:

def crawler(url):
#定义一个crawler()函数。
    r = requests.get(url)
    #用requests.get()函数爬取网站。
    print(url,time.time()-start,r.status_code)
    #打印网址、请求运行时间、状态码。

3)利用循环,将⼀个个爬虫函数和函数需要的变量,放进gevent.spawn()中,从而创建任务task,再把任务task放进任务列表中:

for url in url_list:
#遍历url_list。
    task = gevent.spawn(crawler,url)
    #用gevent.spawn()函数创建任务。
    tasks_list.append(task)
    #往任务列表添加任务。

4)使用gevent.joinall就可以把任务列表中的任务启动执行:

gevent.joinall(tasks_list)
#执行任务列表里的所有任务,就是让爬虫开始爬取网站。

task爬虫任务开始执⾏,接下来就由程序控制task爬⾍任务异步执行即可。 

5)总结:

        其实这里只是个模板⽽已,我们只需要创建爬取⽹站列表,创建爬虫函数,将爬虫函数交给 gevent.spawn分配任务,最后由gevent.joinall执行任务。 使用时只需要将修改网站和爬取的代码即可。


四. queue模块

1)为什么需要要用queue模块

        假设我们要爬取1000个模块,我们可以用我们刚刚学的gevent语法,我们可以用gevent.spawn()创建1000个爬取任务,再用gevent.joinall()执⾏这1000个任务。但这种⽅法会有问题:执行1000个任务,就是⼀下⼦发起1000次请求,这样⼦的恶意请求, 会拖垮网站的服务器。

        所以我们还需要结合队列queue,我们利⽤queue模块来存储任务,让任务都变成⼀条整⻬的队列,就像银⾏窗⼝的排号做法。因为queue其实是⼀种有序的数据结构,可以⽤来存取数据。
        

2)结合队列queue的方法 

老规矩,将结合queue之后的代码再展示一下:

from gevent import monkey
#从gevent库里导入monkey模块。
monkey.patch_all()
#monkey.patch_all()能把程序变成协作式运行,就是可以帮助程序实现异步。
import gevent,time,requests
#导入gevent、time、requests
from gevent.queue import Queue
#从gevent库里导入queue模块

start = time.time() 
#记录程序开始时间。
url_list = ['https://www.baidu.com/', 'https://www.sina.com.cn/', 'http://www.sohu.com/', 'https://www.qq.com/', 
            'https://www.163.com/', 'http://www.iqiyi.com/', 'https://www.tmall.com/', 'http://www.ifeng.com/'] 
work = Queue()
#创建队列对象,并赋值给work。
for url in url_list:
#遍历url_list
    work.put_nowait(url) 
    #用put_nowait()函数可以把网址都放进队列里。
def crawler():
    while not work.empty():
    #当队列不是空的时候,就执行下面的程序。
        url = work.get_nowait()
        #用get_nowait()函数可以把队列里的网址都取出。
    r = requests.get(url)
    #用requests.get()函数抓取网址。
    print(url,work.qsize(),r.status_code)
    #打印网址、队列长度、抓取请求的状态码。
tasks_list = [ ]
#创建空的任务列表
for x in range(2):
#相当于创建了2个爬虫
    task = gevent.spawn(crawler)
    #用gevent.spawn()函数创建执行crawler()函数的任务。
    tasks_list.append(task)
    #往任务列表添加任务。
gevent.joinall(tasks_list)
#用gevent.joinall方法,执行任务列表里的所有任务,就是让爬虫开始爬取网站。
end = time.time()
print(end-start)
  • 导入模块queue:
    from gevent.queue import Queue
    #从gevent库里导入queue模块
  • 导⼊模块,设置爬取的⽹址,然后就开始创建队列 :
    work = Queue()
    #创建队列对象,并赋值给work。
    for url in url_list:
    #遍历url_list
        work.put_nowait(url) 
        #用put_nowait()函数可以把网址都放进队列里。
  • 与此前不同的是,我们是将网址放进队列中,让每个网站领号排序好,然后定义爬取函数:
    def crawler():
        while not work.empty():
        #当队列不是空的时候,就执行下面的程序。
            url = work.get_nowait()
            #用get_nowait()函数可以把队列里的网址都取出。
            r = requests.get(url)
            #用requests.get()函数抓取网址。
            print(url,work.qsize(),r.status_code)
            #打印网址、队列长度、抓取请求的状态码。
  • 通过判断队列是不是空,来判断任务是否完成,以控制是否继续执行任务,最后⼜是熟悉的操作,创建爬虫任务放进gevent.spawn,再使⽤gevent.joinall 将任务执行:
    for x in range(2):
    #相当于创建了2个爬虫
        task = gevent.spawn(crawler)
        #用gevent.spawn()函数创建执行crawler()函数的任务。
        tasks_list.append(task)
        #往任务列表添加任务。
    gevent.joinall(tasks_list)
    #用gevent.joinall方法,执行任务列表里的所有任务,就是让爬虫开始爬取网站。

            这里有⼀点要注意:我们创建了多少个爬虫任务,就会开启多少个协程程序,比如这⾥是两次循环,所以创建了两个task = gevent.spawn(crawler) 爬虫任务,通过两个爬虫任务,从队列中获取网址开始爬取内容,直到队列中没有网址url了。


五. 总结

        程序员写代码并不是从0开始的,我们也是需要借助多个模板拼接,使得代码能够实现我们的想法,而且也并非默写出来,毕竟学习编程是开卷学习,开卷使用,就比如这个多协程队列爬取内容,我们只需要将url_list修改成我们要爬取的网站。

        以上就是今天要讲的内容,本文介绍了爬虫多线程使用的一些相关知识。

        欢迎大家留言一起讨论问题~~~


注:本文是参考风变编程课程资料(已经授权)及部分百度资料整理所得,系博主个人整理知识的文章,如有侵权,请联系博主,感谢~


爬虫文章专栏

https://blog.csdn.net/weixin_53919192/category_11748211.htmlhttps://blog.csdn.net/weixin_53919192/category_11748211.html

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Hulake_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值