import asyncio import random class RateLimiter: """Гарантирует минимальный интервал между вызовами wait(), плюс джиттер. Semaphore ограничивает только количество одновременных запросов - можно держать его хоть в 1, но всё равно долбить сайт пачками сразу как освобождается слот. Этот класс решает именно это: не чаще чем раз в min_interval секунд, сколько бы корутин ни стояло в очереди. """ def __init__(self, min_interval: float, jitter: float = 0.0): self.min_interval = min_interval self.jitter = jitter self._lock = asyncio.Lock() self._last_call = 0.0 async def wait(self): async with self._lock: loop = asyncio.get_event_loop() now = loop.time() delay = self.min_interval + random.uniform(0, self.jitter) sleep_for = delay - (now - self._last_call) if sleep_for > 0: await asyncio.sleep(sleep_for) self._last_call = loop.time()