This commit is contained in:
2026-02-16 10:36:43 +05:00
parent a255e9296f
commit 862113bb65
2 changed files with 112 additions and 82 deletions

View File

@@ -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 Модуль для автоматизации работы с браузером через Selenium
""" """
@@ -260,20 +282,18 @@ class BrowserService:
return False return False
def upload_file(self, file_path, selector='input[type="file"]', by=By.CSS_SELECTOR): def upload_file(self, file_path, selector='input[type="file"]', by=By.CSS_SELECTOR):
"""Загружает файл через input.""" """Загружает файл через input[type='file'] с максимальной надёжностью."""
try: try:
logging.info(f"📤 Загружаю файл: {Path(file_path).name}") 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))) 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) file_input.send_keys(file_path)
logging.info(f"✅ Файл загружен: {Path(file_path).name}") logging.info(f"✅ Файл загружен: {Path(file_path).name}")
time.sleep(1) time.sleep(1)
@@ -395,110 +415,120 @@ class BrowserService:
return False return False
def select_tab_by_multiple_methods(self, tab_text, timeout=15): def select_tab_by_multiple_methods(self, tab_text, timeout=15):
"""Пытается выбрать вкладку на странице перебором всех элементов. """Пытается выбрать вкладку или кнопку на странице перебором всех элементов.
Args: Args:
tab_text (str): Текст вкладки для поиска tab_text (str): Текст вкладки или кнопки для поиска
timeout (int): Общее время ожидания в секундах timeout (int): Общее время ожидания в секундах
Returns: Returns:
bool: True, если вкладка найдена и выбрана, иначе False bool: True, если элемент найден и выбран, иначе False
""" """
from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.action_chains import ActionChains
end_time = time.time() + timeout end_time = time.time() + timeout
attempt = 0 attempt = 0
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'
tab_text_normalized = tab_text.strip()
logging.info(f"🔍 Начинаю поиск вкладки: '{tab_text}' (v2)")
while time.time() < end_time: while time.time() < end_time:
attempt += 1 attempt += 1
logging.info(f" Попытка {attempt}: поиск элементов через JS...") logging.info(f" Попытка {attempt}: поиск элементов через JS...")
try: try:
# Ищем и кликаем через JavaScript в одном скрипте script = f"""
script = """ var searchText = arguments[0];
var searchText = arguments[0].toLowerCase(); var allElements = document.querySelectorAll('{tag_selector}');
var allElements = document.querySelectorAll('a, div, span, li, button, td, th');
var foundElements = []; var foundElements = [];
for (var i = 0; i < allElements.length; i++) {{
for (var i = 0; i < allElements.length; i++) {
var el = allElements[i]; var el = allElements[i];
if (el.offsetParent === null) continue; // Skip hidden if (el.offsetParent === null) continue;
var elementText = (el.innerText || el.textContent || '').trim().toLowerCase();
var elementText = el.innerText || el.textContent || ''; var aria = (el.getAttribute('aria-label') || '').trim().toLowerCase();
var cleanText = elementText.trim().toLowerCase(); var title = (el.getAttribute('title') || '').trim().toLowerCase();
if (
// Strict match or contains if short elementText == searchText ||
if (cleanText === searchText || (cleanText.includes(searchText) && cleanText.length < searchText.length + 50)) { elementText.includes(searchText) ||
aria == searchText ||
aria.includes(searchText) ||
title == searchText ||
title.includes(searchText)
) {{
foundElements.push(el); 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; return foundElements;
""" """
candidates = self.driver.execute_script(script, tab_text_normalized) candidates = self.driver.execute_script(script, tab_text_normalized)
if not candidates: if not candidates:
logging.info(" ⚠️ JS поиск ничего не дал, пробую XPath...") logging.info(" ⚠️ JS поиск ничего не дал, пробую XPath...")
try: 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) candidates = self.driver.find_elements(By.XPATH, xpath)
except: except Exception as e_xpath:
pass logging.warning(f" XPath fail: {e_xpath}")
logging.info(f" Найдено кандидатов: {len(candidates)}") logging.info(f" Найдено кандидатов: {len(candidates)}")
for i, element in enumerate(candidates): for i, element in enumerate(candidates):
try: try:
if not element.is_displayed(): if not element.is_displayed():
continue continue
self.driver.execute_script("arguments[0].style.border='3px solid red';", element)
# Подсветка time.sleep(0.5)
self.driver.execute_script("arguments[0].style.border='3px solid red';", element) try:
time.sleep(0.5) 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} (<{element.tag_name}>): '{element.text[:40]}'") logging.info(f" 👉 Кандидат #{i+1} (<unknown>): {elog}")
# Сохраняем текущее содержимое body
# 1. ActionChains old_body = self.driver.execute_script("return document.body.innerHTML;")
try: # 1. ActionChains
ActionChains(self.driver).move_to_element(element).click().perform() try:
logging.info(" ✅ ActionChains click OK") ActionChains(self.driver).move_to_element(element).click().perform()
self.driver.execute_script("arguments[0].style.border='';", element) logging.info(" ✅ ActionChains click OK")
return True self.driver.execute_script("arguments[0].style.border='';", element)
except Exception as e1: except Exception as e1:
logging.warning(f" ActionChains fail: {e1}") logging.warning(f" ActionChains fail: {e1}")
# 2. Native Click
# 2. Native Click try:
try: element.click()
element.click() logging.info(" ✅ Element click OK")
logging.info(" ✅ Element click OK") self.driver.execute_script("arguments[0].style.border='';", element)
self.driver.execute_script("arguments[0].style.border='';", element) except Exception as e2:
return True logging.warning(f" Native click fail: {e2}")
except Exception as e2: # 3. JS Click
logging.warning(f" Native click fail: {e2}") self.driver.execute_script("arguments[0].click();", element)
logging.info(" ✅ JS click OK")
# 3. JS Click self.driver.execute_script("arguments[0].style.border='';", element)
self.driver.execute_script("arguments[0].click();", element) # Проверяем изменение содержимого body
logging.info(" ✅ JS click OK") try:
self.driver.execute_script("arguments[0].style.border='';", element) WebDriverWait(self.driver, 5).until(lambda d: d.execute_script("return document.body.innerHTML;") != old_body)
return True logging.info(" ✅ Содержимое страницы изменилось после клика")
return True
except Exception:
logging.warning(" ⚠️ Содержимое страницы не изменилось после клика")
continue
except Exception as e: except Exception as e:
logging.error(f" Ошибка обработки элемента: {e}") logging.error(f" Ошибка обработки элемента: {e}")
try: try:
self.driver.execute_script("arguments[0].style.border='';", element) self.driver.execute_script("arguments[0].style.border='';", element)
except: except:
pass pass
time.sleep(1) time.sleep(1)
except Exception as e: except Exception as e:
logging.warning(f" ✗ Ошибка при попытке {attempt}: {e}") logging.warning(f" ✗ Ошибка при попытке {attempt}: {e}")
time.sleep(1) time.sleep(1)
logging.error(f"Не удалось найти и кликнуть на элемент '{tab_text}' за {timeout} секунд")
logging.error(f"Не удалось найти и кликнуть на вкладку '{tab_text}' за {timeout} секунд")
return False return False

4
ui.py
View File

@@ -123,7 +123,7 @@ class UIManager:
self.upload_buttons_entry = tk.Entry(main_frame, font=self.font_label) 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.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( upload_label = tk.Label(
main_frame, main_frame,
@@ -136,7 +136,7 @@ class UIManager:
self.process_buttons_entry = tk.Entry(main_frame, font=self.font_label) 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.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( process_label = tk.Label(
main_frame, main_frame,