如何为aiohttp创建委托/嵌套异步上下文管理器?

时间:2016-05-30 16:41:01

标签: python-3.5 python-asyncio aiohttp

我想为动态等待的抓取工具创建自定义请求管理器 我的爬虫需要向禁止来自同一IP地址的并行请求的站点发出请求。如果发生这种阻塞请求返回HTTP错误代码403,503,429等 如果出现错误,我想等一段时间再重复一次请求。但是为了简化解析器,他们只需调用get并接收正确的页面 我想使用 aiohttp async Python 3.5 的语法,因此我的解析器类可以使用 async with 我的请求者类如果使用 aiohttp.ClientSession ,就会这样:

# somewhere in a parser
async def get_page(self, requester, page_index):
  async with requester.get(URL_FMT.format(page_index)) as response:
    html_content = await response.read()
    result = self.parsing_page(html_content)
    return result

如果请求者 aiohttp.ClientSession ,那么响应 aiohtpp.ClientResponse ,其中 __ aenter__ __ aexit __ 方法,因此 async with 按预期工作。
但是,如果我将请求者类放在中间,它就不再起作用了。

Traceback (most recent call last):
  File "/opt/project/api/tornado_runner.py", line 6, in <module>
    from api import app
  File "/opt/project/api/api.py", line 20, in <module>
    loop.run_until_complete(session.login())
  File "/usr/local/lib/python3.5/asyncio/base_events.py", line 337, in run_until_complete
    return future.result()
  File "/usr/local/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/usr/local/lib/python3.5/asyncio/tasks.py", line 239, in _step
    result = coro.send(None)
  File "/opt/project/api/viudata/session.py", line 72, in login
    async with self.get('https://www.viudata.com') as resp:
AttributeError: __aexit__
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x7f44f61ef240>

看起来像这样。

class Requester:
   def __init__(self, session: aiohttp.ClientSession):
     self.session = session

   async def get(self, *args, **kwargs):
     is_result_successful = False
     while not is_result_successful:
       response = await self.session.get(*args, **kwargs)
       if response.status in [503, 403, 429]:
          await self.wait_some_time()
       else:
          is_result_successful = True
     return response

根据我的理解 self.session.get 是协程功能所以我等待它。结果是 aiohttp.ClientResponse ,其中 __ aenter __ __ aexit __ 。但是如果返回解析器 async with 的代码块返回奇数错误。

aiohttp.ClientSession 一样,您能说出我需要替换为请求者类的内容吗?

1 个答案:

答案 0 :(得分:1)

您应该编写其他代码以支持async with协议。

请参阅client.request()_RequestContextManager获取灵感。