协程是什么
若是咱们想要爬取的是成千上万条的数据,那么就会遇到一个问题:
由于程序是一行一行依次执行的缘故,要等待好久,咱们才能拿到想要的数据
既然一个爬虫爬取大量数据要爬好久,那咱们能不能让多个爬虫一块儿爬取?
一我的干不完的活儿,组个团队一块儿干,活一下被干完了
把三部电影都点击下载。哪一部先下载好了,就先看哪一部,尚未下载完的电影持续保持下载状态
在一个任务未完成时,就能够执行其余多个任务,彼此不受影响(在看第一部下载好的电影时,其余电影继续保持下载状态,彼此之间不受影响),叫异步。
有异步的概念,也有同步的概念, 同步就是一个任务结束才能启动下一个(类比你看完一部电影,才能去看下一部电影)。
显然,异步执行任务会比同步更加节省时间,由于它能减小没必要要的等待。若是你须要对时间作优化,异步是一个很值得考虑的方案。
若是咱们把同步与异步的概念迁移到网络爬虫的场景中,那咱们以前学的爬虫方式都是同步的
爬虫每发起一个请求,都要等服务器返回响应后,才会执行下一步。而不少时候,因为网络不稳定,加上服务器自身也须要响应的时间,致使爬虫会浪费大量时间在等待上。这也是爬取大量数据时,爬虫的速度会比较慢的缘由
咱们能够采起异步的爬虫方式,让多个爬虫在执行任务时保持相对独立,彼此不受干扰,这样就能够免去等待时间, 显然这样爬虫的效率和速度都会提升。
计算机小知识
每台计算机都靠着CPU(中央处理器)干活。在过去,单核CPU的计算机在处理多任务时,会出现一个问题:每一个任务都要抢占CPU,执行完了一个任务才开启下一个任务。CPU毕竟只有一个,这会让计算机处理的效率很低
为了解决这样的问题,一种非抢占式的异步技术被创造了出来,这种方式叫多协程
原理:一个任务在执行过程当中,若是遇到等待,就先去执行其余的任务,当等待结束,再回来继续以前的那个任务。
在计算机的世界,这种任务来回切换得很是快速,看上去就像多个任务在被同时执行同样。
能够在等电饭煲蒸饭的时候去炒菜。而不是等饭作好,再去炒菜。你仍是那个你,但工做时间就这样被缩短了
多协程的用法 gevent库
效果对比: 爬取8个网站(包括百度、新浪、搜狐、腾讯、网易、爱奇艺、天猫、凤凰)
同步的爬虫方式,是依次爬取网站,并等待服务器响应(状态码为200表示正常响应)后,才爬取下一个网站。好比第一个先爬取了百度的网址,等服务器响应后,再去爬取新浪的网址,以此类推,直至所有爬取完毕。
# 导入requests和time
import requests
import time
# 记录程序开始时间
start = time.time()
# 把8个网站封装成列表
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/'
]
# 遍历url_list
for url in url_list:
# 用requests.get()函数爬取网站
r = requests.get(url)
# 打印网址和抓取请求的状态码
print(url, r.status_code)
# 记录程序结束时间
end = time.time()
# end-start是结束时间减去开始时间,就是最终所花时间。
# 最后,把时间打印出来。
print(end-start)
复制代码
试试异步...
# 建议只要使用gevent记得把gevent相关的import语句放在全部其余import语句的前边
# 在最开头的地方gevent.monkey.patch_all();
# 把标准库中的thread/socket等给替换掉.
# 这样咱们在后面使用socket的时候能够跟日常同样使用,无需修改任何代码,可是它变成非阻塞的了
import gevent
from gevent import monkey
monkey.patch_all()
import requests
import time
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/'
]
def crawler(url):
r = requests.get(url)
print(url, time.time()-start, r.status_code)
tasks_list = []
for url in url_list:
task = gevent.spawn(crawler, url)
tasks_list.append(task)
gevent.joinall(tasks_list)
end = time.time()
print(end-start)
复制代码
咱们案例爬取的数据量还比较小,不能直接体现出更大的速度差别。若是爬的是大量的数据,运用多协程会有更显著的速度优点
把爬取
8
个网站变成爬取80
个网站,用同步的爬取方式大概须要花17.3
秒,但用多协程异步爬取只需大概4.5秒,整个爬取效率提高了**280%+
**。
安装 gevent ==>
pip install gevent
以前的代码, 加上注释...
# 导入gevent、time、requests。
import requests
import time
import gevent
# 从gevent库里导入monkey模块。
from gevent import monkey
# monkey.patch_all()能把程序变成协做式运行,就是能够帮助程序实现异步。
monkey.patch_all()
# 记录程序开始时间。
start = time.time()
# 把8个网站封装成列表。
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/'
]
# 定义一个crawler()函数。
def crawler(url):
# 用requests.get()函数爬取网站。
r = requests.get(url)
# 打印网址、请求运行时间、状态码。
print(url, time.time()-start, r.status_code)
# 建立空的任务列表。
tasks_list = []
# 遍历url_list。
for url in url_list:
# 用gevent.spawn()函数建立任务。
task = gevent.spawn(crawler, url)
# 往任务列表添加任务。
tasks_list.append(task)
# 执行任务列表里的全部任务,就是让爬虫开始爬取网站。
gevent.joinall(tasks_list)
# 记录程序结束时间。
end = time.time()
# 打印程序最终所需时间。
print(end-start)
复制代码
gevent.spawn()的参数需为要调用的函数名及该函数的参数。好比,gevent.spawn(crawler,url)就是建立一个执行crawler函数的任务,参数为crawler函数名和它自身的参数url。
调用gevent库里的joinall方法,能启动执行全部的任务。gevent.joinall(tasks_list)就是执行tasks_list这个任务列表里的全部任务,开始爬取。
那若是咱们要爬的不是8个网站,而是1000个网站,咱们能够怎么作?
咱们能够用gevent.spawn()建立1000个爬取任务,再用gevent.joinall()执行这1000个任务。
执行1000个任务,就是一会儿发起1000次请求,这样子的恶意请求,会拖垮网站的服务器
既然这种建立1000个任务的方式不可取,咱们能不能只建立成5个任务,每一个任务爬取200个网站?
这5个任务之间是异步执行的,可是每一个任务(爬取200个网站)内部是同步的
队列(银行叫号) queue模块
当咱们用多协程来爬虫,须要建立大量任务时,咱们能够借助queue模块。
queue翻译成中文是队列的意思。咱们能够用queue模块来存储任务,让任务都变成一条整齐的队列,就像银行窗口的排号作法。由于queue实际上是一种有序的数据结构,能够用来存取数据。
这样,协程就能够从队列里把任务提取出来执行,直到队列空了,任务也就处理完了。就像银行窗口的工做人员会根据排号系统里的排号,处理客人的业务,若是已经没有新的排号,就意味着客户的业务都已办理完毕。
# 从gevent库里导入monkey模块。
import requests
import time
import gevent
from gevent.queue import Queue
from gevent import monkey
# monkey.patch_all()能把程序变成协做式运行,就是能够帮助程序实现异步。
monkey.patch_all()
# 导入gevent、time、requests
# 从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。
work = Queue()
# 遍历url_list
for url in url_list:
# 用put_nowait()函数能够把网址都放进队列里。
work.put_nowait(url)
def crawler():
# 当队列不是空的时候,就执行下面的程序。
while not work.empty():
# 用get_nowait()函数能够把队列里的网址都取出。
url = work.get_nowait()
# 用requests.get()函数抓取网址。
r = requests.get(url)
# 打印网址、队列长度、抓取请求的状态码。
print(url, work.qsize(), r.status_code)
#建立空的任务列表
tasks_list = []
#至关于建立了8个爬虫
for x in range(8):
#用gevent.spawn()函数建立执行crawler()函数的任务。
task = gevent.spawn(crawler)
#往任务列表添加任务。
tasks_list.append(task)
#用gevent.joinall方法,执行任务列表里的全部任务,就是让爬虫开始爬取网站。
gevent.joinall(tasks_list)
end = time.time()
print(end-start)
复制代码
并行和并发
后来,咱们的CPU从单核终于进化到了多核,每一个核都可以独立运做。计算机开始可以真正意义上同时执行多个任务(术语叫并行执行),而不是在多个任务之间来回切换(术语叫并发执行)
咱们电脑通常都会是多核CPU。多协程,其实只占用了CPU的一个核运行,没有充分利用到其余核。利用CPU的多个核同时执行任务的技术,咱们把它叫作“多进程”。
真正大型的爬虫程序不会单单只靠多协程来提高爬取速度的。好比,百度搜索引擎,能够说是超大型的爬虫程序,它除了靠多协程,必定还会靠多进程,甚至是分布式爬虫。
虽然爬虫是异步加多线程的,可是咱们只能在一台主机上运行,因此爬取效率仍是有限的,分布式爬虫则是将多台主机组合起来,共同完成一个爬取任务,这将大大提升爬取的效率
总结
同步和异步
多协程,是一种非抢占式的异步方式。使用多协程,就能让多个爬取任务用异步的方式交替执行。
猫哥教你写爬虫 000--开篇.md
猫哥教你写爬虫 001--print()函数和变量.md
猫哥教你写爬虫 002--做业-打印皮卡丘.md
猫哥教你写爬虫 003--数据类型转换.md
猫哥教你写爬虫 004--数据类型转换-小练习.md
猫哥教你写爬虫 005--数据类型转换-小做业.md
猫哥教你写爬虫 006--条件判断和条件嵌套.md
猫哥教你写爬虫 007--条件判断和条件嵌套-小做业.md
猫哥教你写爬虫 008--input()函数.md
猫哥教你写爬虫 009--input()函数-人工智能小爱同窗.md
猫哥教你写爬虫 010--列表,字典,循环.md
猫哥教你写爬虫 011--列表,字典,循环-小做业.md
猫哥教你写爬虫 012--布尔值和四种语句.md
猫哥教你写爬虫 013--布尔值和四种语句-小做业.md
猫哥教你写爬虫 014--pk小游戏.md
猫哥教你写爬虫 015--pk小游戏(全新改版).md
猫哥教你写爬虫 016--函数.md
猫哥教你写爬虫 017--函数-小做业.md
猫哥教你写爬虫 018--debug.md
猫哥教你写爬虫 019--debug-做业.md
猫哥教你写爬虫 020--类与对象(上).md
猫哥教你写爬虫 021--类与对象(上)-做业.md
猫哥教你写爬虫 022--类与对象(下).md
猫哥教你写爬虫 023--类与对象(下)-做业.md
猫哥教你写爬虫 024--编码&&解码.md
猫哥教你写爬虫 025--编码&&解码-小做业.md
猫哥教你写爬虫 026--模块.md
猫哥教你写爬虫 027--模块介绍.md
猫哥教你写爬虫 028--模块介绍-小做业-广告牌.md
猫哥教你写爬虫 029--爬虫初探-requests.md
猫哥教你写爬虫 030--爬虫初探-requests-做业.md
猫哥教你写爬虫 031--爬虫基础-html.md
猫哥教你写爬虫 032--爬虫初体验-BeautifulSoup.md
猫哥教你写爬虫 033--爬虫初体验-BeautifulSoup-做业.md
猫哥教你写爬虫 034--爬虫-BeautifulSoup实践.md
猫哥教你写爬虫 035--爬虫-BeautifulSoup实践-做业-电影top250.md
猫哥教你写爬虫 036--爬虫-BeautifulSoup实践-做业-电影top250-做业解析.md
猫哥教你写爬虫 037--爬虫-宝宝要听歌.md
猫哥教你写爬虫 038--带参数请求.md
猫哥教你写爬虫 039--存储数据.md
猫哥教你写爬虫 040--存储数据-做业.md
猫哥教你写爬虫 041--模拟登陆-cookie.md
猫哥教你写爬虫 042--session的用法.md
猫哥教你写爬虫 043--模拟浏览器.md
猫哥教你写爬虫 044--模拟浏览器-做业.md
猫哥教你写爬虫 045--协程.md
猫哥教你写爬虫 046--协程-实践-吃什么不会胖.md
猫哥教你写爬虫 047--scrapy框架.md
猫哥教你写爬虫 048--爬虫和反爬虫.md
猫哥教你写爬虫 049--完结撒花.mdhtml