我想为具有动态等待的爬虫创建自定义请求管理器。
我的爬虫需要向禁止来自同一 IP 地址的并行请求的站点发出请求。如果发生这种阻塞,请求返回 HTTP 错误代码 403、503、429 等
。如果出现错误,我想等待一段时间并重复请求。但是为了简单起见,他们只调用 get 并接收正确的页面。
我想将aiohttp和新的async 与Python 3.5的语法一起使用,因此如果他们像这样使用aiohttp.ClientSession,我的解析器类可以以同样的方式对我的请求者类使用async with:
# 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__方法,因此可以按预期异步工作。
但是如果我把我的请求者类放在中间,它就不再工作了。
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__。但是如果返回它parser的async with block 代码返回奇怪的错误。
你能说一下我需要用我的请求者类替换什么,就像用aiohttp.ClientSession 一样吗?