这篇文章主要为大家介绍了python 基于aiohttp的异步爬虫实战详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
引言
钢铁知识库,一个学习python爬虫、数据分析的知识库。人生苦短,快用python。
之前我们使用requests库爬取某个站点的时候,每发出一个请求,程序必须等待网站返回响应才能接着运行,而在整个爬虫过程中,整个爬虫程序是一直在等待的,实际上没有做任何事情。
像这种占用磁盘/内存IO、网络IO的任务,大部分时间是CPU在等待的操作,就叫IO密集型任务。对于这种情况有没有优化方案呢,当然有,那就是使用aiohttp库实现异步爬虫。
aiohttp是什么
我们在使用requests请求时,只能等一个请求先出去再回来,才会发送下一个请求。明显效率不高阿,这时候如果换成异步请求的方式,就不会有这个等待。一个请求发出去,不管这个请求什么时间响应,程序通过await挂起协程对象后直接进行下一个请求。
解决方法就是通过 aiohttp + asyncio,什么是aiohttp?一个基于 asyncio 的异步 HTTP 网络模块,可用于实现异步爬虫,速度明显快于 requests 的同步爬虫。
requests和aiohttp区别
区别就是一个同步一个是异步。话不多说直接上代码看效果。
安装aiohttp
1 |
|
- requests同步示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
可以看到5次请求总共用12.7秒,再来看同样的请求异步多少时间。
- aiohttp异步示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
两次对比可以看到执行过程,时间一个是顺序执行,一个是同时执行。这就是同步和异步的区别。
aiohttp使用介绍
接下来我们会详细介绍aiohttp库的用法和爬取实战。aiohttp 是一个支持异步请求的库,它和 asyncio 配合使用,可以使我们非常方便地实现异步请求操作。asyncio模块,其内部实现了对TCP、UDP、SSL协议的异步操作,但是对于HTTP请求,就需要aiohttp实现了。
aiohttp分为两部分,一部分是Client,一部分是Server。下面来说说aiohttp客户端部分的用法。
基本实例
先写一个简单的案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
aiohttp请求的方法和之前有明显区别,主要包括如下几点:
- 除了导入aiohttp库,还必须引入asyncio库,因为要实现异步,需要启动协程。
- 异步的方法定义不同,前面都要统一加async来修饰。
- with as用于声明上下文管理器,帮我们自动分配和释放资源,加上async代码支持异步。
- 对于返回协程对象的操作,前面需要加await来修饰。response.text()返回的是协程对象。
- 最后运行启用循环事件
注意:Python3.7及以后的版本中,可以使用asyncio.run(main())代替最后的启动操作。
URL参数设置
对于URL参数的设置,我们可以借助params设置,传入一个字典即可,实例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
可以看到实际请求的URL后面带了后缀,这就是params的内容。
请求类型
除了get请求,aiohttp还支持其它请求类型,如POST、PUT、DELETE等,和requests使用方式类似。
1 2 3 4 5 6 |
|
要使用这些方法,只需要把对应的方法和参数替换一下。用法和get类似就不再举例。
响应的几个方法
对于响应来说,我们可以用如下方法分别获取其中的响应情况。状态码、响应头、响应体、响应体二进制内容、响应体JSON结果,实例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
可以看到有些字段前面需要加await,因为其返回的是一个协程对象(如async修饰的方法),那么前面就要加await。
超时设置
我们可以借助ClientTimeout
对象设置超时,例如要设置1秒的超时时间,可以这么实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
这里设置了超时1秒请求延时2秒,发现抛出异常asyncio.TimeoutError
,如果正常则响应200。
并发限制
aiohttp可以支持非常高的并发量,但面对高并发网站可能会承受不住,随时有挂掉的危险,这时需要对并发进行一些控制。现在我们借助asyncio 的Semaphore来控制并发量,实例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
在main方法里,我们声明了1000个task,如果没有通过Semaphore进行并发限制,那这1000放到gather方法后会被同时执行,并发量相当大。有了信号量的控制之后,同时运行的task数量就会被控制,这样就能给aiohttp限制速度了。
aiohttp异步爬取实战
接下来我们通过异步方式练手一个小说爬虫,需求如下:
需求页面:百度小说
目录接口:https://dushu.baidu.com/api/pc/getCatalog?data={"book_id":"4308080950"}
详情接口:
https://dushu.baidu.com/api/pc/getChapterContent?data={"book_id":"4295122774","cid":"4295122774|116332"}
关键参数:book_id
:小说ID、cid
:章节id
采集要求:使用协程方式写入,数据存放进mongo
需求分析:点开需求页面,通过F12抓包可以发现两个接口。一个目录接口,一个详情接口。
首先第一步先请求目录接口拿到cid章节id,然后将cid传递给详情接口拿到小说数据,最后存入mongo即可。
话不多说,直接上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
|
至此,我们就使用aiohttp完成了对小说章节的爬取。
要实现异步处理,得先要有挂起操作,当一个任务需要等待 IO 结果的时候,可以挂起当前任务,转而去执行其他任务,这样才能充分利用好资源,要实现异步,需要了解 await 的用法,使用 await 可以将耗时等待的操作挂起,让出控制权。当协程执行的时候遇到 await,时间循环就会将本协程挂起,转而去执行别的协程,直到其他的协程挂起或执行完毕。
await 后面的对象必须是如下格式之一:
- A native coroutine object returned from a native coroutine function,一个原生 coroutine 对象。
- A generator-based coroutine object returned from a function decorated with types.coroutine,一个由 types.coroutine 修饰的生成器,这个生成器可以返回 coroutine 对象。
- An object with an await method returning an iterator,一个包含 await 方法的对象返回的一个迭代器。
总结
以上就是借助协程async和异步aiohttp两个主要模块完成异步爬虫的内容,
aiohttp 以异步方式爬取网站的耗时远小于 requests 同步方式,以上列举的例子希望对你有帮助。
注意,线程和协程是两个概念,后面找机会我们再聊聊进程和线程、线程和协程的关系
300+Python经典编程案例
50G+学习视频教程
100+Python初阶、中阶、高阶电子书籍
1000+简历模板和汇报PPT模板(转正、年终等)