查看原文
其他

Python异步爬虫详解

2017-11-30

作者 大邓

一、同步与异步

异步编程可以大幅度的提高系统的吞吐量,提高单位时间内发出的请求数目。之前大邓写的爬虫都是同步,就是对aurl发起请求,等待响应。然后再访问burl,等待响应。。。

大量的时间消耗在等待上,如果能近似的同时对多个网址发起请求,等待响应,速度回快很多倍。这里的同时其实是为了让大家便于理解,其实所谓的同时也是有先后顺序的,所以叫异步。

文字描述太别扭,我们水平方向代表时间

#同步编程(同一时间只能做一件事,做完了才能做下一件事情)
<-a_url-><-b_url-><-c_url->

#异步编程 (可以近似的理解成同一时间有多个事情在做,但有先后)
<-a_url->  <-b_url->    <-c_url->      <-d_url->        <-e_url->          <-f_url->            <-g_url->              <-h_url->                <--i_url-->                  <--j_url-->

你看同步编程同样的时间访问了3个网址,但是异步编程访问的效率提高了很多倍。

二、async&await

在python3.5之后,开始引入async和await语法结构,通过async关键词定义一个协程(不用管啥名字,总之就理解成异步吧)。其实我也有很多地方不熟悉,没关系,先会用,孰而知之。

2.1 并不成功的套路

网上看了些教程,总结出来大概是以下的模板:

import asyncio

#函数名:做现在的任务时不等待,能继续做别的任务。
async def donow_meantime_dontwait(url):    response = await requests.get(url)


#函数名:快速高效的做任务
async def fast_do_your_thing():    await asyncio.wait([donow_meantime_dontwait(url) for url in urls])    
#下面两行都是套路,记住就好
loop = asyncio.get_event_loop()
loop.run_until_complete(fast_do_your_thing())

将模板改造,开始实验

import asyncio
import requests
import time urls = ['https://book.douban.com/tag/小说','https://book.douban.com/tag/科幻',        'https://book.douban.com/tag/漫画','https://book.douban.com/tag/奇幻',        'https://book.douban.com/tag/历史','https://book.douban.com/tag/经济学']

async def requests_meantime_dont_wait(url):    print(url)    response = await requests.get(url)    print("{url} 得到响应".format(url=url))


async def fast_requsts():    start = time.time()    await asyncio.wait([requests_meantime_dont_wait(url) for url in urls])    end = time.time()    print("耗时 {} 秒".format(end - start))


loop = asyncio.get_event_loop()
loop.run_until_complete(fast_requsts())

正以为成功了,出现bug。requests库返回的响应对象不能使用await关键词。

TypeError: object Response can't be used in 'await' expression

百度一下,await表达式中的对象必须是awaitable。

PEP 492 -- Coroutines with async and await syntax https://www.python.org/dev/peps/pep-0492/

awaitable对象

  • A native coroutine object returned from a native coroutine function

  • A generator-based coroutine object returned from a function decorated with types.coroutine() .

  • An object with an await method returning an iterator.

又百度了下,说是封装下就可变成原生异步函数。不懂啥意思,还是copy代码直接运行试试吧。

import asyncio
import requests
import time urls = ['https://book.douban.com/tag/小说','https://book.douban.com/tag/科幻',        'https://book.douban.com/tag/漫画','https://book.douban.com/tag/奇幻',        'https://book.douban.com/tag/历史','https://book.douban.com/tag/经济学']


async def requests_meantime_dont_wait(url):    """注意这里没有await关键词"""    resp = requests.get(url)    print(resp.status_code)    

async def package_requests(url):    """将requests_meantime_dont_wait封装成awaitbale。"""    print(url)    await requests_meantime_dont_wait(url)    print("{url} 得到响应".format(url=url))


async def fast_requsts(urls):    start = time.time()    await asyncio.wait([package_requests(url) for url in urls])    end = time.time()    print("Complete in {} seconds".format(end - start))


loop = asyncio.get_event_loop()
loop.run_until_complete(fast_requsts(urls))loop.close()
https://book.douban.com/tag/经济学
200
https://book.douban.com/tag/经济学 得到响应 https://book.douban.com/tag/奇幻
200
https://book.douban.com/tag/奇幻 得到响应 https://book.douban.com/tag/历史
200
https://book.douban.com/tag/历史 得到响应 https://book.douban.com/tag/漫画
200
https://book.douban.com/tag/漫画 得到响应 https://book.douban.com/tag/小说
200
https://book.douban.com/tag/小说 得到响应 https://book.douban.com/tag/科幻
200
https://book.douban.com/tag/科幻 得到响应
Complete in 3.8158602714538574 seconds

代码正常运行,可是打印出来的东西不对啊。应该是几个网址在一起,然后是200状态吗在一起才对的。这应该不是异步,后来去掉异步的这些关键词单独运行,运行效率没有啥大区别。

2.2 终于找到正确的套路

继续百度,奥,requests不支持非阻塞。即使使用异步编程,requests库还是按照同步那样访问完aurl,得到响应后才再访问burl。。。。

坑爹啊,继续百度发现aiohttp,用于异步请求的库。还是不懂,陆陆续续,经过数小时的百度,copy,试运行,终于成品出来了。

import asyncio
import requests
import time
import aiohttp

#urls = ['https://book.douban.com/tag/小说','https://book.douban.com/tag/科幻',        #'https://book.douban.com/tag/漫画','https://book.douban.com/tag/奇幻',        #'https://book.douban.com/tag/历史','https://book.douban.com/tag/经济学']

urls = ['http://www.jianshu.com/p/b59453dd38cf','http://www.jianshu.com/','http://pbpython.com/pathlib-intro.html']

async def requests_meantime_dont_wait(url):    print(url)    async with aiohttp.ClientSession() as session:        async with session.get(url) as resp:            print(resp.status)            print("{url} 得到响应".format(url=url))


async def fast_requsts(urls):    start = time.time()    await asyncio.wait([requests_meantime_dont_wait(url) for url in urls])    end = time.time()    print("Complete in {} seconds".format(end - start))


loop = asyncio.get_event_loop()
loop.run_until_complete(fast_requsts(urls))loop.close()

由于我的行径对豆瓣太恶劣了,访问次数过多。出现一些异常,在写文字时随便抽取了三个网址作为测试用的网址。结果很符合预期,实现了异步,先对多个网址发起请求,慢慢的等待结果的到来。

http://www.jianshu.com/p/b59453dd38cf http://pbpython.com/pathlib-intro.html http://www.jianshu.com/ 200 http://www.jianshu.com/ 得到响应 200 http://www.jianshu.com/p/b59453dd38cf 得到响应 200 http://pbpython.com/pathlib-intro.html 得到响应 Complete in 1.152311086654663 seconds


历史文章:

数据采集

pyquery爬取豆瓣读书

selenium驱动器配置详解

icrawler:强大简单的图片爬虫库

抓取单博主的所有微博及其评论

爬虫神器PyQuery的使用方法

pyquery一些自定义的用法

【视频】猫途鹰网站评论数据抓取

【视频讲解】在scrapy框架中如何使用selenium?

【实战视频】使用scrapy写爬虫-爬知乎live

简易SQLite3数据库学习

【视频教程】对视频进行基本操作

【视频】抓包小练习-B站评论数据抓取

fake-useragent库:值得花2分钟学习的库

【视频】爬取饿了么平台上的门店信息

如何抓取视频资源-以头条视频为例

当爬虫遭遇验证码,怎么办

【视频】手把手教你抓美女~

【视频】有了selenium,小白也可以自豪的说:“去TMD的抓包、cookie”

【视频】快来get新技能--抓包+cookie,爬微博不再是梦

【视频教程】用python批量抓取简书用户信息

识别假货有绝招,就用python爬淘宝评论

用Python抓取百度地图里的店名,地址和联系方式

文本处理分析

gensim:用Word2Vec进行文本分析

RAKE:快速自动抽取关键词算法

对于中文,nltk能做哪些事情

用nltk计算词语间的点互信息值PMI

Python NLP中的五大英雄

用SenticNet库做细粒度情感分析

如何对csv文件中的文本数据进行分析处理

复杂网络分析数据准备篇

文本分析之网络关系

用词云图解读“于欢案”

基于共现发现人物关系的python实现

用pyecharts制作词云图

图片数据处理

OpenCV:快速入门图片人脸识别

好玩的OpenCV:图片操作的基本知识(1)

好玩的OpenCV:图像操作的基本知识(2)

OpenCV:计算图片有多色

如何对数据进行各种排序?

其他

迅雷不给力,我DIY了个下载器

【视频】初学者必看:python环境配置

开扒皮自己微信的秘密

初识装饰器函数

计算运行时间-装饰器实现

花十分钟,给爱机安装个MongoDB

chardet库:轻松识别文件的编码格式

使用Python登录QQ邮箱发送QQ邮件

WTF Python: 开启你的懵逼模式

8行代码实现微信聊天机器人

使用Python+OpenCV进行面部合成

十分钟学会用Flask发邮件


十分钟入门常用的json库


优雅简洁的列表推导式

【资源帖】简单易懂的机器学习视频教程


链接: https://pan.baidu.com/s/1bpttgb9 密码: kxw5

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存