diff --git a/services/browser_service.py b/services/browser_service.py index 7bcaf45..ea2accd 100644 --- a/services/browser_service.py +++ b/services/browser_service.py @@ -1,3 +1,25 @@ +class BrowserService: + """Сервис для автоматизации работы с браузером.""" + # ...existing code... + + def click_tab_and_wait(self, url, tab_text, wait_before_click=5, wait_after_click=5): + """Открывает страницу, ждёт, нажимает вкладку по тексту, ждёт загрузку.""" + try: + logging.info(f"🌐 Открываю страницу: {url}") + self.driver.get(url) + logging.info(f"⏳ Жду {wait_before_click} сек перед кликом...") + time.sleep(wait_before_click) + logging.info(f"🔍 Нажимаю вкладку: '{tab_text}'") + result = self.select_tab_by_multiple_methods(tab_text, timeout=10) + if result: + logging.info(f"✅ Вкладка '{tab_text}' нажата, жду {wait_after_click} сек...") + time.sleep(wait_after_click) + else: + logging.warning(f"❌ Не удалось нажать вкладку '{tab_text}'") + return result + except Exception as e: + logging.error(f"❌ Ошибка click_tab_and_wait: {e}") + return False """ Модуль для автоматизации работы с браузером через Selenium """ @@ -260,20 +282,18 @@ class BrowserService: return False def upload_file(self, file_path, selector='input[type="file"]', by=By.CSS_SELECTOR): - """Загружает файл через input.""" + """Загружает файл через input[type='file'] с максимальной надёжностью.""" try: logging.info(f"📤 Загружаю файл: {Path(file_path).name}") - - # Попытка кликнуть на область загрузки - try: - drop_area = self.driver.find_element(By.CSS_SELECTOR, '.upload-area, .drop-zone, [data-upload]') - drop_area.click() - time.sleep(1) - logging.info(" Найдена область загрузки") - except: - pass - file_input = self.wait.until(EC.presence_of_element_located((by, selector))) + # Если input скрыт, делаем его видимым через JS + is_displayed = file_input.is_displayed() + if not is_displayed: + try: + self.driver.execute_script("arguments[0].style.display = 'block'; arguments[0].style.visibility = 'visible'; arguments[0].style.opacity = 1; arguments[0].style.height = 'auto'; arguments[0].style.width = 'auto';", file_input) + logging.info(" Сделал input[type='file'] видимым через JS") + except Exception as e: + logging.warning(f" Не удалось сделать input[type='file'] видимым: {e}") file_input.send_keys(file_path) logging.info(f"✅ Файл загружен: {Path(file_path).name}") time.sleep(1) @@ -395,110 +415,120 @@ class BrowserService: return False def select_tab_by_multiple_methods(self, tab_text, timeout=15): - """Пытается выбрать вкладку на странице перебором всех элементов. + """Пытается выбрать вкладку или кнопку на странице перебором всех элементов. Args: - tab_text (str): Текст вкладки для поиска + tab_text (str): Текст вкладки или кнопки для поиска timeout (int): Общее время ожидания в секундах Returns: - bool: True, если вкладка найдена и выбрана, иначе False + bool: True, если элемент найден и выбран, иначе False """ from selenium.webdriver.common.action_chains import ActionChains - + end_time = time.time() + timeout attempt = 0 - - # Нормализуем текст для поиска - tab_text_normalized = tab_text.strip() - - logging.info(f"🔍 Начинаю поиск вкладки: '{tab_text}' (v2)") - + tab_text_normalized = tab_text.strip().lower() + logging.info(f"🔍 Начинаю поиск вкладки/кнопки: '{tab_text}' (v3)") + + tag_selector = 'a, div, span, li, button, td, th, input, label, strong, b, em, i, p, section, header, footer, article, aside, nav, form' + while time.time() < end_time: attempt += 1 logging.info(f" Попытка {attempt}: поиск элементов через JS...") - try: - # Ищем и кликаем через JavaScript в одном скрипте - script = """ - var searchText = arguments[0].toLowerCase(); - var allElements = document.querySelectorAll('a, div, span, li, button, td, th'); + script = f""" + var searchText = arguments[0]; + var allElements = document.querySelectorAll('{tag_selector}'); var foundElements = []; - - for (var i = 0; i < allElements.length; i++) { + for (var i = 0; i < allElements.length; i++) {{ var el = allElements[i]; - if (el.offsetParent === null) continue; // Skip hidden - - var elementText = el.innerText || el.textContent || ''; - var cleanText = elementText.trim().toLowerCase(); - - // Strict match or contains if short - if (cleanText === searchText || (cleanText.includes(searchText) && cleanText.length < searchText.length + 50)) { + if (el.offsetParent === null) continue; + var elementText = (el.innerText || el.textContent || '').trim().toLowerCase(); + var aria = (el.getAttribute('aria-label') || '').trim().toLowerCase(); + var title = (el.getAttribute('title') || '').trim().toLowerCase(); + if ( + elementText == searchText || + elementText.includes(searchText) || + aria == searchText || + aria.includes(searchText) || + title == searchText || + title.includes(searchText) + ) {{ foundElements.push(el); - } - } + }} + var children = el.querySelectorAll('span, b, strong, em, i'); + for (var j = 0; j < children.length; j++) {{ + var childText = (children[j].innerText || children[j].textContent || '').trim().toLowerCase(); + if (childText == searchText || childText.includes(searchText)) {{ + foundElements.push(el); + break; + }} + }} + }} return foundElements; """ - candidates = self.driver.execute_script(script, tab_text_normalized) - if not candidates: logging.info(" ⚠️ JS поиск ничего не дал, пробую XPath...") try: - xpath = f"//*[contains(text(), '{tab_text}')]" + xpath = ( + f"//*[contains(translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '{tab_text_normalized}') " + f"or contains(translate(@aria-label, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '{tab_text_normalized}') " + f"or contains(translate(@title, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '{tab_text_normalized}')]" + ) candidates = self.driver.find_elements(By.XPATH, xpath) - except: - pass + except Exception as e_xpath: + logging.warning(f" XPath fail: {e_xpath}") logging.info(f" Найдено кандидатов: {len(candidates)}") - for i, element in enumerate(candidates): try: - if not element.is_displayed(): - continue - - # Подсветка - self.driver.execute_script("arguments[0].style.border='3px solid red';", element) - time.sleep(0.5) - - logging.info(f" 👉 Кандидат #{i+1} (<{element.tag_name}>): '{element.text[:40]}'") - - # 1. ActionChains - try: - ActionChains(self.driver).move_to_element(element).click().perform() - logging.info(" ✅ ActionChains click OK") - self.driver.execute_script("arguments[0].style.border='';", element) - return True - except Exception as e1: - logging.warning(f" ActionChains fail: {e1}") - - # 2. Native Click - try: - element.click() - logging.info(" ✅ Element click OK") - self.driver.execute_script("arguments[0].style.border='';", element) - return True - except Exception as e2: - logging.warning(f" Native click fail: {e2}") - - # 3. JS Click - self.driver.execute_script("arguments[0].click();", element) - logging.info(" ✅ JS click OK") - self.driver.execute_script("arguments[0].style.border='';", element) - return True - + if not element.is_displayed(): + continue + self.driver.execute_script("arguments[0].style.border='3px solid red';", element) + time.sleep(0.5) + try: + logging.info(f" 👉 Кандидат #{i+1} (<{element.tag_name}>): text='{element.text[:40]}', aria-label='{element.get_attribute('aria-label')}', title='{element.get_attribute('title')}'") + except Exception as elog: + logging.info(f" 👉 Кандидат #{i+1} (): {elog}") + # Сохраняем текущее содержимое body + old_body = self.driver.execute_script("return document.body.innerHTML;") + # 1. ActionChains + try: + ActionChains(self.driver).move_to_element(element).click().perform() + logging.info(" ✅ ActionChains click OK") + self.driver.execute_script("arguments[0].style.border='';", element) + except Exception as e1: + logging.warning(f" ActionChains fail: {e1}") + # 2. Native Click + try: + element.click() + logging.info(" ✅ Element click OK") + self.driver.execute_script("arguments[0].style.border='';", element) + except Exception as e2: + logging.warning(f" Native click fail: {e2}") + # 3. JS Click + self.driver.execute_script("arguments[0].click();", element) + logging.info(" ✅ JS click OK") + self.driver.execute_script("arguments[0].style.border='';", element) + # Проверяем изменение содержимого body + try: + WebDriverWait(self.driver, 5).until(lambda d: d.execute_script("return document.body.innerHTML;") != old_body) + logging.info(" ✅ Содержимое страницы изменилось после клика") + return True + except Exception: + logging.warning(" ⚠️ Содержимое страницы не изменилось после клика") + continue except Exception as e: logging.error(f" Ошибка обработки элемента: {e}") try: self.driver.execute_script("arguments[0].style.border='';", element) except: pass - time.sleep(1) - except Exception as e: logging.warning(f" ✗ Ошибка при попытке {attempt}: {e}") time.sleep(1) - - logging.error(f"❌ Не удалось найти и кликнуть на вкладку '{tab_text}' за {timeout} секунд") + logging.error(f"❌ Не удалось найти и кликнуть на элемент '{tab_text}' за {timeout} секунд") return False \ No newline at end of file diff --git a/ui.py b/ui.py index aa33b5a..7bed810 100644 --- a/ui.py +++ b/ui.py @@ -123,7 +123,7 @@ class UIManager: self.upload_buttons_entry = tk.Entry(main_frame, font=self.font_label) self.upload_buttons_entry.pack(anchor="w", fill=tk.X, pady=(0, 5)) - self.upload_buttons_entry.insert(0, "Загрузить файл, Выбрать файл, Добавить файл") + self.upload_buttons_entry.insert(0, "Выберите файл, Выбрать файл, Добавить файл") upload_label = tk.Label( main_frame, @@ -136,7 +136,7 @@ class UIManager: self.process_buttons_entry = tk.Entry(main_frame, font=self.font_label) self.process_buttons_entry.pack(anchor="w", fill=tk.X, pady=(0, 5)) - self.process_buttons_entry.insert(0, "Загрузить, Обработать, Преобразовать, Изменить, Конвертировать") + self.process_buttons_entry.insert(0, "Исправить, Обработать, Преобразовать, Изменить, Конвертировать") process_label = tk.Label( main_frame,