diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a2b60e --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +.venv +venv +__pycache__ +*.pyc +*.pyo +*.pyd +*.sqlite3 +*.log +.DS_Store +.env +.env.* +.idea +.vscode +*.swp +*.swo +coverage +htmlcov +.tox +.pytest_cache +.pyre +.mypy_cache +.pytype +.pytest_cache/ +.cache +.pyre/ +.mypy_cache/ +.pytype/ +dist +build +*.egg-info +.eggs +pip-wheel-metadata +pip-wheel-metadata/ +instance +.webassets-cache +.scrapy +.scrapy/ +.pytest_cache/ +.coverage +.coverage.* +nosetests.xml +coverage.xml diff --git a/.python-version b/.python-version old mode 100644 new mode 100755 diff --git a/README.md b/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/app.py b/app.py new file mode 100644 index 0000000..5b7da19 --- /dev/null +++ b/app.py @@ -0,0 +1,15 @@ +""" +Главная точка входа приложения для автоматизации загрузки файлов +""" +import logging +from ui import main + +if __name__ == "__main__": + # Настройка логирования + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' + ) + + # Запуск интерфейса + main() diff --git a/main.py b/main.py deleted file mode 100644 index e19e8a3..0000000 --- a/main.py +++ /dev/null @@ -1,121 +0,0 @@ -import sys -import time -import logging -from selenium import webdriver -from selenium.webdriver.common.by import By -from selenium.webdriver.firefox.service import Service as FirefoxService -from selenium.webdriver.firefox.options import Options as FirefoxOptions -from selenium.webdriver.common.action_chains import ActionChains -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC - -# Настройка логирования в терминал -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') - -def setup_driver(download_dir): - """Настраивает и возвращает веб-драйвер с опциями для загрузки файлов.""" - try: - firefox_options = FirefoxOptions() - firefox_options.set_preference("browser.download.folderList", 2) - firefox_options.set_preference("browser.download.dir", download_dir) - firefox_options.set_preference("browser.download.useDownloadDir", True) - firefox_options.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream") - # firefox_options.add_argument("--headless") - firefox_options.binary_location = r"C:\Program Files\Mozilla Firefox\firefox.exe" - logging.info("Запуск Firefox драйвера...") - driver = webdriver.Firefox(options=firefox_options) - driver.set_page_load_timeout(300) # Timeout для загрузки страницы - logging.info("Драйвер запущен успешно.") - return driver - except Exception as e: - logging.error(f"Ошибка при настройке драйвера: {e}") - raise - -def log_page_content(driver): - """Логирует содержимое страницы в терминал.""" - try: - title = driver.title - logging.info(f"Заголовок страницы: {title}") - # Попробуем найти основной контент - try: - main_content = driver.find_element(By.TAG_NAME, 'h1').text - logging.info(f"Основной заголовок: {main_content}") - except: - logging.info("Основной заголовок не найден.") - logging.info("Логирование содержимого завершено.") - except Exception as e: - logging.error(f"Ошибка при получении содержимого страницы: {e}") - -def upload_file(driver, file_path, selector='input[type="file"]', by=By.CSS_SELECTOR): - """Загружает файл в input по селектору.""" - try: - # Сначала попробуем кликнуть на видимую область загрузки - try: - drop_area = driver.find_element(By.CSS_SELECTOR, '.upload-area, .drop-zone, [data-upload]') - drop_area.click() - time.sleep(1) - except: - pass - file_input = WebDriverWait(driver, 10).until(EC.presence_of_element_located((by, selector))) - file_input.send_keys(file_path) - logging.info(f"Файл загружен: {file_path}") - except Exception as e: - logging.error(f"Ошибка при загрузке файла {file_path}: {e}") - -def close_cookie_banner(driver): - """Закрывает cookie-баннер, если он есть.""" - try: - logging.info("Попытка закрыть cookie-баннер...") - logging.info("Ищу кнопки...") - buttons = driver.find_elements(By.TAG_NAME, 'button') - logging.info(f"Найдено кнопок: {len(buttons)}") - for button in buttons: - text = button.text.lower() - logging.info(f"Кнопка: {text}") - if 'принять' in text or 'accept' in text or 'ok' in text or 'соглас' in text: - button.click() - logging.info("Cookie-баннер закрыт.") - time.sleep(1) - return - # Если не нашли, попробуем по классу или id - try: - driver.find_element(By.ID, 'cookie-accept').click() - logging.info("Cookie-баннер закрыт по ID.") - except: - try: - driver.find_element(By.CLASS_NAME, 'cookie-accept').click() - logging.info("Cookie-баннер закрыт по классу.") - except: - logging.info("Cookie-баннер не найден.") - except Exception as e: - logging.error(f"Ошибка при закрытии cookie-баннера: {e}") - -def main(): - url = "https://convertio.co/ru/document-converter/" - file_path = "c:\\Users\\jze9\\Documents\\git\\test-loderweb\\test.txt" - download_dir = "C:\\Users\\jze9\\Downloads" # Укажите путь для загрузок - - driver = setup_driver(download_dir) - try: - logging.info(f"Переход на страницу: {url}") - driver.get(url) - logging.info("Страница загружена.") - time.sleep(5) # Ждем загрузки - - # close_cookie_banner(driver) # Закрываем cookie-баннер - - log_page_content(driver) - - # Загрузить файл - upload_file(driver, file_path) - - # Возможно, нажать кнопку конвертации, если нужно - # click_button(driver, 'button.convert') # Пример селектора - - time.sleep(300) # Ждем дольше для симуляции - input("Нажмите Enter для выхода...") # Чтобы браузер не закрывался сразу - finally: - logging.info("Программа завершена, браузер остаётся открытым.") - -if __name__ == "__main__": - main() diff --git a/pyproject.toml b/pyproject.toml old mode 100644 new mode 100755 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..94b985f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +selenium==4.40.0 +webdriver-manager==4.0.2 diff --git a/services/__init__.py b/services/__init__.py new file mode 100644 index 0000000..a70b302 --- /dev/null +++ b/services/__init__.py @@ -0,0 +1 @@ +# Services package diff --git a/services/browser_service.py b/services/browser_service.py new file mode 100644 index 0000000..cfa5b4e --- /dev/null +++ b/services/browser_service.py @@ -0,0 +1,380 @@ +""" +Модуль для автоматизации работы с браузером через 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: Поиск по тегу