Как установить максимальное количество запросов в секунду (ограничить их) на стороне клиента с помощью aiohttp?
Aiohttp: установить максимальное количество запросов в секунду
Ответ 1
Я нашел одно возможное решение здесь: http://compiletoi.net/fast-scraping-in-python-with-asyncio.html
Выполнение 3 запросов в то же время классно, но 5000, однако, не так приятно. Если вы попытаетесь сделать слишком много запросов одновременно, соединения могут начать закрываться, или вы даже можете быть заблокированы с веб-сайта.
Чтобы этого избежать, вы можете использовать семафор. Это инструмент синхронизации, который можно использовать для ограничения количества сопрограмм, которые что-то делают в какой-то момент. Мы просто создадим семафор перед созданием цикла, передавая в качестве аргумента количество одновременных запросов, которые хотим разрешить:
sem = asyncio.Semaphore(5)
Тогда мы просто заменим:
page = yield from get(url, compress=True)
тем же, но защищенным семафором:
with (yield from sem):
page = yield from get(url, compress=True)
Это обеспечит одновременное выполнение не более 5 запросов.
Ответ 2
Так как 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)
Ответ 3
Вы можете установить задержку для каждого запроса или сгруппировать URL-адреса в пакеты и регулировать пакеты в соответствии с желаемой частотой.
1. Задержка на запрос
asyncio.sleep
скрипт ждать между запросами используя 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())
.
2. Дроссель партии
Используя 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())
.