fix
This commit is contained in:
@@ -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)")
|
||||||
tab_text_normalized = tab_text.strip()
|
|
||||||
|
tag_selector = 'a, div, span, li, button, td, th, input, label, strong, b, em, i, p, section, header, footer, article, aside, nav, form'
|
||||||
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
4
ui.py
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user