import asyncio
from collections import deque
from datetime import datetime
import time
from playwright.async_api import Page
class NetworkIdleWaiter:
"""
Класс для продвинутого ожидания.
Запускает таймер и сбрасывает его после каждого загруженного файла.
Спустя заданное время после последней активности разблокирует поток.
"""
def __init__(
self,
page: Page,
idle_time: float = 1.0,
timeout: float = 10.0,
ignore_resources: list = None,
print_on: bool = False,
):
self.page = page
self.idle_time = idle_time
self.timeout = timeout
self.ignore_resources = ignore_resources or ['font', 'websocket']
self.pending_requests = {}
self.last_activity = time.time()
self.network_log = deque(maxlen=100) # Последние 100 запросов для отладки
self.print_on = print_on
async def wait(self):
"""Ждет пока сеть станет неактивной"""
# Подписываемся на события
self.page.on("request", self._on_request)
self.page.on("response", self._on_response)
self.page.on("requestfailed", self._on_request_failed)
start_time = time.time()
try:
while True:
await asyncio.sleep(0.1)
current_time = time.time()
idle_duration = current_time - self.last_activity
# Проверяем условия выхода
if len(self.pending_requests) == 0 and idle_duration >= self.idle_time:
if self.print_on:
print(f"✅ Сеть неактивна {idle_duration:.1f} сек. "
f"Загрузка завершена!")
break
# Проверка таймаута
if current_time - start_time > self.timeout:
if self.print_on:
print(f"⚠️ Таймаут {self.timeout} сек. "
f"Остались запросы: {len(self.pending_requests)}")
if self.pending_requests:
print("Незавершенные запросы:")
for url, info in list(self.pending_requests.items())[:5]:
print(f" - {info['type']}: {url[:100]}...")
break
# Отладочная информация
if self.print_on:
if len(self.pending_requests) > 0:
print(f"⏳ Ожидание... Активных запросов: {len(self.pending_requests)}, "
f"Idle: {idle_duration:.1f}s")
finally:
# Отписываемся от событий
self.page.remove_listener("request", self._on_request)
self.page.remove_listener("response", self._on_response)
self.page.remove_listener("requestfailed", self._on_request_failed)
def get_stats(self):
"""Получить статистику по сетевой активности"""
return {
'total_requests': len(self.network_log),
'pending': len(self.pending_requests),
'last_activity': self.last_activity,
'log': list(self.network_log)
}
# Обработчики событий
def _on_request(self, request):
# Игнорируем определенные типы ресурсов
if request.resource_type in self.ignore_resources:
return
self.pending_requests[request.url] = {
'start': time.time(),
'type': request.resource_type,
'method': request.method
}
self.last_activity = time.time()
self.network_log.append({
'event': 'request',
'url': request.url,
'type': request.resource_type,
'time': datetime.now().isoformat()
})
def _on_response(self, response):
if response.url in self.pending_requests:
request_info = self.pending_requests.pop(response.url)
duration = time.time() - request_info['start']
self.network_log.append({
'event': 'response',
'url': response.url,
'status': response.status,
'duration': duration,
'time': datetime.now().isoformat()
})
self.last_activity = time.time()
def _on_request_failed(self, request):
if request.url in self.pending_requests:
self.pending_requests.pop(request.url)
self.network_log.append({
'event': 'failed',
'url': request.url,
'failure': request.failure,
'time': datetime.now().isoformat()
})
self.last_activity = time.time()