【问题标题】:How to create delegate/nested async context manager for aiohttp?如何为 aiohttp 创建委托/嵌套异步上下文管理器?
【发布时间】:2016-05-30 16:41:01
【问题描述】:

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

# 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

如果 requesteraiohttp.ClientSession,那么 responseaiohtpp.ClientResponse__aenter__ __aexit__ 方法,因此 async with 可以按预期工作。
但是如果我把我的 requester 类放在中间,它就不再工作了。

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 是协程函数,所以我会等待它。结果是具有 __aenter____aexit__aiohttp.ClientResponse。但是如果返回 parserasync with 代码块返回奇怪的错误。

你能说我需要用 aiohttp.ClientSession 类替换我的 requester 类吗?

【问题讨论】:

标签: python-3.5 python-asyncio aiohttp


【解决方案1】:

您应该编写额外的代码来支持async with 协议。

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

【讨论】:

  • 感谢您指出我需要从哪里开始。但令人遗憾的是,新语法糖产生的问题比它解决的问题多。特别是用于实现简单的装饰器。
  • 常规的python函数和上下文管理器都不是,你需要做一些技巧让它既可以作为函数又可以作为上下文管理器。
猜你喜欢
  • 1970-01-01
  • 2016-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-24
相关资源
最近更新 更多