异步爬虫: async/await 与 aiohttp的使用,以及例子
来源:互联网 发布:淘宝网宽松牛仔裤女款 编辑:程序博客网 时间:2024/06/08 17:56
在python3.5中,加入了asyncio/await 关键字,使得回调的写法更加直观和人性化。而aiohttp是一个提供异步web服务的库,分为服务器端和客户端。这里主要使用其客户端。本文分为三步分,第一部分简单介绍python3.5的异步,asyncio/await 关键字。第二部分介绍aiohttp客户端部分的使用。第三部分是一个例子,列举了如何爬取CSDN某个博客中的所有文章。
1. async/await
async/await 是 python3.5中新加入的特性, 将异步从原来的yield 写法中解放出来,变得更加直观。
在3.5之前,如果想要使用异步,主要使用yield语法。举例如下:
import asyncio@asyncio.coroutine # 修饰符,等同于 asyncio.coroutine(hello())def hello(): print('Hello world! (%s)' % threading.currentThread()) yield from asyncio.sleep(1) # 执行到这一步以后,直接切换到下一个任务,等到一秒后再切回来 print('Hello again! (%s)' % threading.currentThread())loop = asyncio.get_event_loop() tasks = [hello(), hello()]loop.run_until_complete(asyncio.wait(tasks))loop.close()
其实上面的例子由于使用了asyncio库以及用coroutine进行修饰,已经比较简化了。而引入了async/await以后,hello()可以写成这样:
async def hello(): print("Hello world!") r = await asyncio.sleep(1) print("Hello again!")
注意此时已经不需要使用@asyncio.coroutin进行修饰,而是在def之前加async表示这是个异步函数,其内有异步操作。此外,使用await 替换了yield from, 表示这一步为异步操作。
2. aiohttp
aiohttp是一个用于web服务的库,网上的资料,包括廖雪峰的网站中的资料大部分是关于服务器端(server)的,关于客户端(client)的资料不多。事实上aiohttp的资料并不完善,官网上只有一些例子,我就也就照着例子依葫芦画瓢了。
2.1 基本用法
async with aiohttp.get('https://github.com') as r: await r.text()
其中r.text(), 可以在括号中指定解码方式,编码方式,例如
await resp.text(encoding='windows-1251')
或者也可以选择不编码,适合读取图像等,是无法编码的
await resp.read()
这里要注意的是with…as的语法,不过这个跟本文无关,读者可以自行搜索了解。
2.2 设置timeout
需要加一个with aiohttp.Timeout(x)
with aiohttp.Timeout(0.001): async with aiohttp.get('https://github.com') as r: await r.text()
2.3 使用session获取数据
这里要引入一个类,aiohttp.ClientSession. 首先要建立一个session对象,然后用该session对象去打开网页。session可以进行多项操作,比如post, get, put, head等等,如下面所示
async with aiohttp.ClientSession() as session: async with session.get('https://api.github.com/events') as resp: print(resp.status) print(await resp.text())
如果要使用post方法,则相应的语句要改成
session.post('http://httpbin.org/post', data=b'data')
2.4 自定义headers
这个比较简单,将headers放于session.get/post的选项中即可。注意headers数据要是dict格式
url = 'https://api.github.com/some/endpoint'headers = {'content-type': 'application/json'}await session.get(url, headers=headers)
显然,该方法对于post等其他方法也都有效
2.5 使用代理
要实现这个功能,需要在生产session对象的过程中做一些修改。
conn = aiohttp.ProxyConnector(proxy="http://some.proxy.com")session = aiohttp.ClientSession(connector=conn)async with session.get('http://python.org') as resp: print(resp.status)
这边没有写成with….. as….形式,但是原理是一样的,也可以很容易的改写成之前的格式
如果代理需要认证,则需要再加一个proxy_auth选项。
conn = aiohttp.ProxyConnector( proxy="http://some.proxy.com", proxy_auth=aiohttp.BasicAuth('user', 'pass'))session = aiohttp.ClientSession(connector=conn)async with session.get('http://python.org') as r: assert r.status == 200
2.6 自定义cookie
同样是在session中做修改。
url = 'http://httpbin.org/cookies'async with ClientSession({'cookies_are': 'working'}) as session: async with session.get(url) as resp: assert await resp.json() == {"cookies": {"cookies_are": "working"}}
3. 样例
在看完第二部分的各功能的用法以后,完成一个例子其实已经很简单了,无非就是将各个功能排列组合而已。下面这个简单的爬虫,是用来爬取我博客下所有的文章的
import urllib.request as requestfrom bs4 import BeautifulSoup as bsimport asyncioimport aiohttp@asyncio.coroutineasync def getPage(url,res_list): print(url) headers = {'User-Agent':'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'} # conn = aiohttp.ProxyConnector(proxy="http://127.0.0.1:8087") async with aiohttp.ClientSession() as session: async with session.get(url,headers=headers) as resp: assert resp.status==200 res_list.append(await resp.text())class parseListPage(): def __init__(self,page_str): self.page_str = page_str def __enter__(self): page_str = self.page_str page = bs(page_str,'lxml') # 获取文章链接 articles = page.find_all('div',attrs={'class':'article_title'}) art_urls = [] for a in articles: x = a.find('a')['href'] art_urls.append('http://blog.csdn.net'+x) return art_urls def __exit__(self, exc_type, exc_val, exc_tb): passpage_num = 5page_url_base = 'http://blog.csdn.net/u014595019/article/list/'page_urls = [page_url_base + str(i+1) for i in range(page_num)]loop = asyncio.get_event_loop()ret_list = []tasks = [getPage(host,ret_list) for host in page_urls]loop.run_until_complete(asyncio.wait(tasks))articles_url = []for ret in ret_list: with parseListPage(ret) as tmp: articles_url += tmpret_list = []tasks = [getPage(url, ret_list) for url in articles_url]loop.run_until_complete(asyncio.wait(tasks))loop.close()
- 异步爬虫: async/await 与 aiohttp的使用,以及例子
- 使用 Async 和 Await 的异步编程
- c# async、await的使用,异步方法
- 关于Promise与async/await的例子
- U3d 使用 异步 async await
- ES7的Async/Await 异步
- ES7的Async/Await 异步
- Async 与Await的简单使用
- C# Async与Await的使用
- .NET中async与await的使用
- C# Async与Await的使用
- .NET中使用异步Async和Await的代价
- async,await本质以及与Task的关系。
- 关于U3d 使用 async await异步语法
- Async and await的使用
- Promise 、Async/Await的使用
- Promise、Async/Await的使用
- Async和Await异步编程的原理
- linux下通过jni编译CUDA代码生成动态链接库遇到的问题 nvcc -fPIC
- CSU 1021 组合数末尾的零
- sort
- JVM虚拟机结构
- centOS7如何关闭防火墙
- 异步爬虫: async/await 与 aiohttp的使用,以及例子
- 神经网络 手写识别例子 matlab实现
- CSS和HTML的四种结合方式
- Hihocoder #1050 : 树中的最长路 (两次dfs 或 一次dfs)
- 嵌入式开发第33天:(TCP/IP协议,面试题内容之一)
- MQ消息队列
- java中的算法
- ThreadLocal内存泄漏详细分析
- iOS 关于window释放