diff --git a/rag/utils/web_utils.py b/rag/utils/web_utils.py index 734b977..e9abed0 100644 --- a/rag/utils/web_utils.py +++ b/rag/utils/web_utils.py @@ -2,7 +2,11 @@ """ Утилиты для загрузки веб-страниц и PDF, извлечения основного текста, рекурсивного обхода ссылок. -Добавлено ограничение размера загружаемого контента. + +Использует aiohttp для асинхронных запросов, BeautifulSoup для парсинга, +readability-lxml для извлечения контента (опционально). + +Добавлено ограничение размера загружаемого контента (max_size_mb). """ import asyncio @@ -29,18 +33,24 @@ logger = logging.getLogger(__name__) async def fetch_any_url(url: str, timeout: int = 30, max_size_mb: int = 5) -> Tuple[Optional[str], Optional[str]]: """ - Универсальная загрузка контента по URL с ограничением размера. + Универсальная загрузка контента по URL. Аргументы: - url: целевой адрес. - timeout: таймаут запроса в секундах. - max_size_mb: максимальный размер загружаемого контента в МБ. + url (str): целевой адрес. + timeout (int): таймаут запроса в секундах. + max_size_mb (int): максимальный размер загружаемого контента в МБ. Возвращает: Tuple[Optional[str], Optional[str]]: (заголовок, текст). Для HTML: заголовок страницы и очищенный основной текст. Для PDF: имя файла (базовая часть URL) и извлечённый текст. В случае ошибки возвращает (None, None). + + Примечания: + - Для PDF используется временный файл, который удаляется после чтения. + - Поддерживаются только HTTP/HTTPS. + - Таймаут применяется к запросу в целом (чтение + соединение). + - Размер контента ограничен параметром max_size_mb. """ try: async with aiohttp.ClientSession() as session: @@ -48,10 +58,10 @@ async def fetch_any_url(url: str, timeout: int = 30, max_size_mb: int = 5) -> Tu if resp.status != 200: logger.error(f"HTTP {resp.status} для {url}") return None, None - content_type = resp.headers.get('Content-Type', '').lower() limit = max_size_mb * 1024 * 1024 + # Ограничение размера: читаем только limit байт try: data = await resp.content.read(limit=limit) except aiohttp.ContentTooLong: @@ -60,6 +70,7 @@ async def fetch_any_url(url: str, timeout: int = 30, max_size_mb: int = 5) -> Tu # Обработка PDF if 'application/pdf' in content_type or url.lower().endswith('.pdf'): + # Сохраняем PDF во временный файл with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as tmp: tmp_path = tmp.name async with aiofiles.open(tmp_path, 'wb') as f: @@ -89,8 +100,28 @@ async def fetch_any_url(url: str, timeout: int = 30, max_size_mb: int = 5) -> Tu def extract_text_from_html(html: str, url: str = "") -> Tuple[Optional[str], str]: - """Извлекает заголовок и основной текст из HTML (без изменений).""" + """ + Извлекает заголовок и основной текст из HTML. + + Аргументы: + html (str): исходный HTML-код. + url (str): URL страницы (используется для формирования заголовка, если теги отсутствуют). + + Возвращает: + Tuple[Optional[str], str]: (заголовок, очищенный текст). + Заголовок может быть None, если не удалось извлечь. + + Алгоритм извлечения заголовка (по приоритету): + 1.