协程是一种用户态的轻量级线程,使用协程有众多好处:
第一个好处是协程像一种在程序级别模拟系统级别的进程,由于是单线程,并且少了上下文切换,因此相对来说系统消耗很少,而且网上的各种测试也表明协程确实拥有惊人的速度。
第二个好处是协程方便切换控制流,这就简化了编程模型。协程能保留上一次调用时的状态(所有局部状态的一个特定组合),每次过程重入时,就相当于进入了上一次调用的状态。
第三个好处是协程的高扩展性和高并发性,一个CPU支持上万协程都不是问题,所以很适合用于高并发处理。
协程也有缺点。
第一,协程的本质是一个单线程,不能同时使用单个CPU的多核,需要和进程配合才能运行在多CPU
上。
第二,有长时间阻塞的操作时不要用协程,因为可能会阻塞整个程序。
在 Pythongevent的协程中可以使用库。 gevent也可以使用pip安装:
pip install gevent
安装完 gevent,就可以使用 gevent进行爬虫了,代码如下:
import gevent
from gevent.queue import Queue, Empty
import time
import requests
from gevent import monkey
link_list = []
# 把下面有可能有I0操作的单独做上标记
monkey.patch_all() # 将IO转为异步执行的函数
with open('alexa.txt', 'r') as file:
file_list = file.readlines()
for each in file_list:
link = each.replace('\n', '')
link_list.append(link)
def crawler(index):
process_id = 'Process--' + str(index)
while not workQueue.empty():
url = workQueue.get(timeout=2)
try:
r = requests.get(url, timeout=20)
print(process_id, workQueue.qsize(), r.status_code, url)
except Exception as e:
print(process_id, workQueue.qsize(), url, 'Error:', e)
def boss():
for url in link_list:
workQueue.put_nowait(url)
if __name__ == '__main__':
workQueue = Queue(1010)
gevent.spawn(boss).join()
start = time.time()
jobs = []
for i in range(10):
jobs.append(gevent.spawn(crawler, i))
gevent.joinall(jobs)
end = time.time()
print('gevent' + 'Queue多协程爬虫的总时间为:', end - start)
print('Main Ended!')
在上述代码中,我们首先使用了
from gevent import monkey
monkey. patch_all()
这样可以实现爬虫的并发能力,如果没有这两句,整个抓取过程就会变成依次抓取。 geventmonkey库中的能把可能有l0操作的单独做上标记,将1O变成可以异步执行的函数。我们还是用 Queue创建队列,但是在
gevent中需要使用:gevent. spawn(boss).join()将队列中加入的内容整合到 gevent中。接下来使用如下代码创建多协程的爬虫程序:
for i in range(10):
jobs.append(gevent.spawn(crawler, i))
gevent.joinall(jobs)
运行上述代码,可以发现开了10个协程,爬虫的速度变得非常快,多协程爬虫能够很好地支持高并发的
工作。