""" Модуль для автоматизации работы с браузером через Selenium """ import time import logging import os import sys import platform import subprocess from pathlib import Path from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.firefox.options import Options as FirefoxOptions from selenium.webdriver.firefox.service import Service as FirefoxService from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC try: from webdriver_manager.firefox import GeckoDriverManager HAS_WEBDRIVER_MANAGER = True except ImportError: HAS_WEBDRIVER_MANAGER = False class BrowserService: """Сервис для автоматизации работы с браузером.""" def __init__(self, download_dir, firefox_binary_path=None): """Инициализирует браузер с указанными параметрами.""" self.download_dir = download_dir self.firefox_binary_path = firefox_binary_path or self._find_firefox_path() self.driver = None self.wait = None self._setup_driver() @staticmethod def _find_firefox_path(): """Автоматически находит путь к Firefox для текущей ОС.""" system = platform.system() found_path = None if system == "Windows": # Windows пути - разные места установки Firefox possible_paths = [ r"C:\Program Files\Mozilla Firefox\firefox.exe", r"C:\Program Files (x86)\Mozilla Firefox\firefox.exe", os.path.expandvars(r"%PROGRAMFILES%\Mozilla Firefox\firefox.exe"), os.path.expandvars(r"%PROGRAMFILES(X86)%\Mozilla Firefox\firefox.exe"), os.path.expandvars(r"%LOCALAPPDATA%\Mozilla Firefox\firefox.exe"), ] # Пытаемся найти через реестр (Windows) try: import winreg try: reg_path = r"SOFTWARE\Mozilla\Mozilla Firefox" with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, reg_path) as key: install_dir = winreg.QueryValueEx(key, "InstallDir")[0] possible_paths.insert(0, os.path.join(install_dir, "firefox.exe")) except: pass except: pass elif system == "Darwin": # macOS пути possible_paths = [ "/Applications/Firefox.app/Contents/MacOS/firefox", "/usr/local/bin/firefox", ] else: # Linux # Linux пути possible_paths = [ "/snap/bin/firefox", "/usr/bin/firefox", "/usr/local/bin/firefox", "/opt/firefox/firefox", ] # Ищем Firefox в возможных местах for path in possible_paths: if path and os.path.exists(path): logging.info(f"✅ Firefox найден: {path}") return path # Пытаемся найти через which/where команды try: if system == "Windows": result = subprocess.run(["where", "firefox.exe"], capture_output=True, text=True, timeout=5) else: result = subprocess.run(["which", "firefox"], capture_output=True, text=True, timeout=5) if result.returncode == 0 and result.stdout.strip(): firefox_path = result.stdout.strip().split('\n')[0] logging.info(f"✅ Firefox найден через {system} search: {firefox_path}") return firefox_path except: pass logging.warning(f"⚠️ Firefox не найден в стандартных местах для {system}") return None def _setup_driver(self): """Настраивает и запускает веб-драйвер.""" try: firefox_options = FirefoxOptions() # ===== ОПЦИИ ЗАГРУЗКИ ===== firefox_options.set_preference("browser.download.folderList", 2) firefox_options.set_preference("browser.download.dir", self.download_dir) firefox_options.set_preference("browser.download.useDownloadDir", True) firefox_options.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream") # ===== ОТКЛЮЧАЕМ HEADLESS (окно ДОЛЖНО быть видимым) ===== # Убедимся, что headless режим НЕ установлен # (это заставит браузер открыться в нормальном режиме) # Устанавливаем путь к Firefox только если он найден if self.firefox_binary_path: logging.info(f" ✅ Используется Firefox: {self.firefox_binary_path}") firefox_options.binary_location = self.firefox_binary_path else: logging.info(" ⚠️ Firefox путь не определен, буду использовать системный поиск...") logging.info("🚀 Запуск Firefox драйвера с ВИДИМЫМ окном...") # Попытка 1: Использовать webdriver-manager (самый надежный способ) if HAS_WEBDRIVER_MANAGER: try: logging.info(" Попытка 1: webdriver-manager...") service = FirefoxService(GeckoDriverManager().install()) self.driver = webdriver.Firefox(service=service, options=firefox_options) except Exception as e: logging.warning(f" webdriver-manager не сработал: {e}") self.driver = None # Попытка 2: Прямой запуск (Selenium сам найдет geckodriver в PATH) if not self.driver: try: logging.info(" Попытка 2: Прямой запуск (Selenium ищет geckodriver в PATH)...") self.driver = webdriver.Firefox(options=firefox_options) except Exception as e: logging.warning(f" Прямой запуск не сработал: {e}") self.driver = None if not self.driver: raise Exception("Не удалось инициализировать Firefox драйвер ни одним способом") # Даем браузеру время открыться time.sleep(2) self.driver.set_page_load_timeout(300) self.wait = WebDriverWait(self.driver, 10) logging.info("✅ Браузер открыт и готов к работе.") except Exception as e: logging.error(f"❌ Ошибка при настройке драйвера: {e}") logging.error(f"\n Решения:") logging.error(f" 1. Убедитесь, что Firefox установлен") logging.error(f" 2. На Windows: установите Firefox с https://mozilla.org/firefox") raise def open_page(self, url): """Открывает страницу по URL.""" try: logging.info(f"🌐 Переход на страницу: {url}") self.driver.get(url) logging.info("✅ Страница загружена.") time.sleep(2) except Exception as e: logging.error(f"❌ Ошибка при открытии страницы: {e}") raise def wait_page_load(self, seconds=5): """Ждет загрузки страницы.""" logging.info(f"⏳ Ожидание {seconds} сек...") time.sleep(seconds) def click_button_by_text(self, button_text, exact_match=False, timeout=10): """ Ищет и нажимает кнопку по тексту в различных формах. Args: button_text (str): Текст кнопки для поиска exact_match (bool): Точное совпадение или частичное timeout (int): Время ожидания в секундах Returns: bool: True если кнопка найдена и нажата, False иначе """ try: logging.info(f"🔎 Ищу кнопку: '{button_text}'") button = self._find_button_by_text(button_text, exact_match, timeout) if button: logging.info(f"✅ Кнопка найдена, нажимаю...") # Скролл к кнопке если нужно self.driver.execute_script("arguments[0].scrollIntoView(true);", button) time.sleep(0.5) button.click() logging.info(f"✅ Кнопка '{button_text}' нажата успешно.") time.sleep(1) return True else: logging.warning(f"⚠️ Кнопка '{button_text}' не найдена.") return False except Exception as e: logging.error(f"❌ Ошибка при нажатии кнопки '{button_text}': {e}") return False def _find_button_by_text(self, button_text, exact_match=False, timeout=10): """ Находит кнопку по тексту различными способами. Args: button_text (str): Текст для поиска exact_match (bool): Точное совпадение timeout (int): Время ожидания Returns: WebElement или None """ button_text_lower = button_text.lower().strip() end_time = time.time() + timeout while time.time() < end_time: # Вариант 1: Поиск по тегу