Aiohttpを使用してクライアント側で1秒あたりのリクエストの最大数を設定(制限)するにはどうすればよいですか?
私はここで1つの可能な解決策を見つけました: http://compiletoi.net/fast-scraping-in-python-with-asyncio.html
3つのリクエストを同時に実行するのはかっこいいですが、5000を実行するのはそれほどいいことではありません。同時に多くのリクエストを実行しようとすると、接続が切断され始めたり、Webサイトから禁止されたりする場合があります。
これを回避するには、セマフォを使用できます。これは、ある時点で何かを行うコルーチンの数を制限するために使用できる同期ツールです。ループを作成する前にセマフォを作成し、許可する同時リクエストの数を引数として渡します。
sem = asyncio.Semaphore(5)
次に、単に置き換えます:
page = yield from get(url, compress=True)
同じことですが、セマフォによって保護されています:
with (yield from sem):
page = yield from get(url, compress=True)
これにより、最大5つのリクエストを同時に実行できます。
V2.0以降、 ClientSession
を使用すると、aiohttp
は同時接続数を自動的に100に制限します。
独自の TCPConnector
を作成してClientSession
に渡すことにより、制限を変更できます。たとえば、50の同時リクエストに制限されたクライアントを作成するには:
import aiohttp
connector = aiohttp.TCPConnector(limit=50)
client = aiohttp.ClientSession(connector=connector)
ユースケースにより適している場合は、limit_per_Host
パラメーター(デフォルトではオフ)を渡して、同時接続数を同じ「エンドポイント」に制限できます。ドキュメントごと:
limit_per_Host
(int
)–同じエンドポイントへの同時接続の制限。(Host, port, is_ssl)
トリプルが等しい場合、エンドポイントは同じです。
使用例:
import aiohttp
connector = aiohttp.TCPConnector(limit_per_Host=50)
client = aiohttp.ClientSession(connector=connector)
リクエストごとに遅延を設定したり、URLをバッチにグループ化したり、バッチを調整して目的の頻度に合わせたりできます。
_asyncio.sleep
_を使用して、スクリプトをリクエスト間で待機させます
_import asyncio
import aiohttp
delay_per_request = 0.5
urls = [
# put some URLs here...
]
async def app():
tasks = []
for url in urls:
tasks.append(asyncio.ensure_future(make_request(url)))
await asyncio.sleep(delay_per_request)
results = await asyncio.gather(*tasks)
return results
async def make_request(url):
print('$$$ making request')
async with aiohttp.ClientSession() as sess:
async with sess.get(url) as resp:
status = resp.status
text = await resp.text()
print('### got page data')
return url, status, text
_
これは、例えばresults = asyncio.run(app())
。
上記の_make_request
_を使用して、次のようにURLのバッチを要求および抑制できます。
_import asyncio
import aiohttp
import time
max_requests_per_second = 0.5
urls = [[
# put a few URLs here...
],[
# put a few more URLs here...
]]
async def app():
results = []
for i, batch in enumerate(urls):
t_0 = time.time()
print(f'batch {i}')
tasks = [asyncio.ensure_future(make_request(url)) for url in batch]
for t in tasks:
d = await t
results.append(d)
t_1 = time.time()
# Throttle requests
batch_time = (t_1 - t_0)
batch_size = len(batch)
wait_time = (batch_size / max_requests_per_second) - batch_time
if wait_time > 0:
print(f'Too fast! Waiting {wait_time} seconds')
time.sleep(wait_time)
return results
_
繰り返しますが、これはasyncio.run(app())
で実行できます。