動的に待機するクローラー用のカスタム リクエスト マネージャーを作成したいと考えています。
私のクローラーは、同じ IP アドレスからの並列リクエストを禁止するサイトにリクエストを送信する必要があります。このようなブロックが発生した場合、リクエストは HTTP エラー コード 403、503、429 などで返されます。エラーが発生した
場合は、しばらく待ってからリクエストを繰り返します。しかし、パーサーを単純にするために、get を呼び出して正しいページを受け取るだけです。Python 3.5の構文でaiohttpと新しいasync
を使用したいので、パーサー クラスはaiohttp.ClientSessionを次のように使用した場合と同じように、リクエスター クラスに対してasyncを使用できます。
# 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の場合、レスポンスは__aenter__および__aexit__メソッドを持つaiohtpp.ClientResponseであるため、非同期は期待どおりに動作します。
しかし、リクエスタークラスを真ん中に置くと、もう機能しません。
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です。しかし、ブロックを返すパーサーの非同期コードを返すと、奇妙なエラーが返されます。
aiohttp.ClientSessionのように、リクエスタークラスで何を置き換える必要があるか教えていただけますか?