ff1
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import platform
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -37,7 +38,16 @@ class BrowserService:
|
|||||||
def _find_firefox_path():
|
def _find_firefox_path():
|
||||||
"""Автоматически находит путь к Firefox для текущей ОС."""
|
"""Автоматически находит путь к Firefox для текущей ОС."""
|
||||||
system = platform.system()
|
system = platform.system()
|
||||||
found_path = None
|
|
||||||
|
# 1. Проверка через shutil.which (Поиск в PATH) - работает и на Win и на Linux
|
||||||
|
path_from_which = shutil.which("firefox")
|
||||||
|
if path_from_which:
|
||||||
|
# На Windows shutil.which может вернуть .EXE, проверим существует ли
|
||||||
|
if os.path.exists(path_from_which):
|
||||||
|
logging.info(f"✅ Firefox найден в PATH: {path_from_which}")
|
||||||
|
return path_from_which
|
||||||
|
|
||||||
|
possible_paths = []
|
||||||
|
|
||||||
if system == "Windows":
|
if system == "Windows":
|
||||||
# Windows пути - разные места установки Firefox
|
# Windows пути - разные места установки Firefox
|
||||||
@@ -47,6 +57,7 @@ class BrowserService:
|
|||||||
os.path.expandvars(r"%PROGRAMFILES%\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"%PROGRAMFILES(X86)%\Mozilla Firefox\firefox.exe"),
|
||||||
os.path.expandvars(r"%LOCALAPPDATA%\Mozilla Firefox\firefox.exe"),
|
os.path.expandvars(r"%LOCALAPPDATA%\Mozilla Firefox\firefox.exe"),
|
||||||
|
os.path.expandvars(r"%APPDATA%\Mozilla Firefox\firefox.exe"),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Пытаемся найти через реестр (Windows)
|
# Пытаемся найти через реестр (Windows)
|
||||||
@@ -72,10 +83,12 @@ class BrowserService:
|
|||||||
else: # Linux
|
else: # Linux
|
||||||
# Linux пути
|
# Linux пути
|
||||||
possible_paths = [
|
possible_paths = [
|
||||||
"/snap/bin/firefox",
|
|
||||||
"/usr/bin/firefox",
|
"/usr/bin/firefox",
|
||||||
|
"/usr/bin/firefox-esr",
|
||||||
|
"/snap/bin/firefox",
|
||||||
"/usr/local/bin/firefox",
|
"/usr/local/bin/firefox",
|
||||||
"/opt/firefox/firefox",
|
"/opt/firefox/firefox",
|
||||||
|
"/usr/lib/firefox/firefox",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Ищем Firefox в возможных местах
|
# Ищем Firefox в возможных местах
|
||||||
@@ -84,17 +97,16 @@ class BrowserService:
|
|||||||
logging.info(f"✅ Firefox найден: {path}")
|
logging.info(f"✅ Firefox найден: {path}")
|
||||||
return path
|
return path
|
||||||
|
|
||||||
# Пытаемся найти через which/where команды
|
# Последний шанс: subprocess (если shutil.which пропустил что-то специфичное)
|
||||||
try:
|
try:
|
||||||
if system == "Windows":
|
cmd = ["where", "firefox.exe"] if system == "Windows" else ["which", "firefox"]
|
||||||
result = subprocess.run(["where", "firefox.exe"], capture_output=True, text=True, timeout=5)
|
result = subprocess.run(cmd, 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():
|
if result.returncode == 0 and result.stdout.strip():
|
||||||
firefox_path = result.stdout.strip().split('\n')[0]
|
firefox_path = result.stdout.strip().split('\n')[0]
|
||||||
logging.info(f"✅ Firefox найден через {system} search: {firefox_path}")
|
if os.path.exists(firefox_path):
|
||||||
return firefox_path
|
logging.info(f"✅ Firefox найден через команду search: {firefox_path}")
|
||||||
|
return firefox_path
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -112,10 +124,6 @@ class BrowserService:
|
|||||||
firefox_options.set_preference("browser.download.useDownloadDir", True)
|
firefox_options.set_preference("browser.download.useDownloadDir", True)
|
||||||
firefox_options.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream")
|
firefox_options.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream")
|
||||||
|
|
||||||
# ===== ОТКЛЮЧАЕМ HEADLESS (окно ДОЛЖНО быть видимым) =====
|
|
||||||
# Убедимся, что headless режим НЕ установлен
|
|
||||||
# (это заставит браузер открыться в нормальном режиме)
|
|
||||||
|
|
||||||
# Устанавливаем путь к Firefox только если он найден
|
# Устанавливаем путь к Firefox только если он найден
|
||||||
if self.firefox_binary_path:
|
if self.firefox_binary_path:
|
||||||
logging.info(f" ✅ Используется Firefox: {self.firefox_binary_path}")
|
logging.info(f" ✅ Используется Firefox: {self.firefox_binary_path}")
|
||||||
@@ -126,20 +134,45 @@ class BrowserService:
|
|||||||
logging.info("🚀 Запуск Firefox драйвера с ВИДИМЫМ окном...")
|
logging.info("🚀 Запуск Firefox драйвера с ВИДИМЫМ окном...")
|
||||||
|
|
||||||
# Попытка 1: Использовать webdriver-manager (самый надежный способ)
|
# Попытка 1: Использовать webdriver-manager (самый надежный способ)
|
||||||
if HAS_WEBDRIVER_MANAGER:
|
if HAS_WEBDRIVER_MANAGER and not self.driver:
|
||||||
try:
|
try:
|
||||||
logging.info(" Попытка 1: webdriver-manager...")
|
logging.info(" Попытка 1: webdriver-manager...")
|
||||||
service = FirefoxService(GeckoDriverManager().install())
|
service = FirefoxService(GeckoDriverManager().install())
|
||||||
self.driver = webdriver.Firefox(service=service, options=firefox_options)
|
self.driver = webdriver.Firefox(service=service, options=firefox_options)
|
||||||
|
logging.info(" ✅ Успешно запущено через webdriver-manager")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning(f" webdriver-manager не сработал: {e}")
|
logging.warning(f" webdriver-manager не сработал: {e}")
|
||||||
self.driver = None
|
self.driver = None
|
||||||
|
|
||||||
# Попытка 2: Прямой запуск (Selenium сам найдет geckodriver в PATH)
|
# Попытка 2: Локальный драйвер в папке проекта
|
||||||
|
if not self.driver:
|
||||||
|
logging.info(" Попытка 2: Поиск локального geckodriver...")
|
||||||
|
system = platform.system()
|
||||||
|
driver_filename = "geckodriver.exe" if system == "Windows" else "geckodriver"
|
||||||
|
|
||||||
|
possible_driver_paths = [
|
||||||
|
os.path.join(os.getcwd(), driver_filename),
|
||||||
|
os.path.join(os.getcwd(), "drivers", driver_filename),
|
||||||
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", driver_filename), # В корне проекта
|
||||||
|
]
|
||||||
|
|
||||||
|
for dp in possible_driver_paths:
|
||||||
|
if os.path.exists(dp):
|
||||||
|
try:
|
||||||
|
logging.info(f" ⚠️ Нашел локальный драйвер: {dp}")
|
||||||
|
service = FirefoxService(executable_path=dp)
|
||||||
|
self.driver = webdriver.Firefox(service=service, options=firefox_options)
|
||||||
|
logging.info(" ✅ Успешно запущено через локальный драйвер")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(f" Ошибка локального драйвера {dp}: {e}")
|
||||||
|
|
||||||
|
# Попытка 3: Прямой запуск (Selenium сам найдет geckodriver в PATH)
|
||||||
if not self.driver:
|
if not self.driver:
|
||||||
try:
|
try:
|
||||||
logging.info(" Попытка 2: Прямой запуск (Selenium ищет geckodriver в PATH)...")
|
logging.info(" Попытка 3: Прямой запуск (Selenium ищет geckodriver в PATH)...")
|
||||||
self.driver = webdriver.Firefox(options=firefox_options)
|
self.driver = webdriver.Firefox(options=firefox_options)
|
||||||
|
logging.info(" ✅ Успешно запущено через системный PATH")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning(f" Прямой запуск не сработал: {e}")
|
logging.warning(f" Прямой запуск не сработал: {e}")
|
||||||
self.driver = None
|
self.driver = None
|
||||||
@@ -154,6 +187,18 @@ class BrowserService:
|
|||||||
self.wait = WebDriverWait(self.driver, 10)
|
self.wait = WebDriverWait(self.driver, 10)
|
||||||
logging.info("✅ Браузер открыт и готов к работе.")
|
logging.info("✅ Браузер открыт и готов к работе.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"❌ Ошибка при настройке драйвера: {e}")
|
||||||
|
logging.error(f"\n Решения:")
|
||||||
|
logging.error(f" 1. Убедитесь, что Firefox установлен")
|
||||||
|
logging.error(f" 2. Скачайте geckodriver для вашей ОС и положите рядом с программой")
|
||||||
|
logging.error(f" 3. Ссылка на драйвер: https://github.com/mozilla/geckodriver/releases")
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
self.driver.set_page_load_timeout(300)
|
||||||
|
self.wait = WebDriverWait(self.driver, 10)
|
||||||
|
logging.info("✅ Браузер открыт и готов к работе.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"❌ Ошибка при настройке драйвера: {e}")
|
logging.error(f"❌ Ошибка при настройке драйвера: {e}")
|
||||||
logging.error(f"\n Решения:")
|
logging.error(f"\n Решения:")
|
||||||
@@ -180,119 +225,24 @@ class BrowserService:
|
|||||||
|
|
||||||
def click_button_by_text(self, button_text, exact_match=False, timeout=10):
|
def click_button_by_text(self, button_text, exact_match=False, timeout=10):
|
||||||
"""
|
"""
|
||||||
Ищет и нажимает кнопку по тексту в различных формах.
|
Ищет и нажимает кнопку (или любой элемент) по тексту, используя продвинутый метод поиска.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
button_text (str): Текст кнопки для поиска
|
button_text (str): Текст кнопки для поиска
|
||||||
exact_match (bool): Точное совпадение или частичное
|
exact_match (bool): Игнорируется в новой реализации (используется умный поиск)
|
||||||
timeout (int): Время ожидания в секундах
|
timeout (int): Время ожидания в секундах
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True если кнопка найдена и нажата, False иначе
|
bool: True если кнопка найдена и нажата, False иначе
|
||||||
"""
|
"""
|
||||||
try:
|
logging.info(f"🔎 Ищу кнопку/элемент с текстом: '{button_text}' (Robust)")
|
||||||
logging.info(f"🔎 Ищу кнопку: '{button_text}'")
|
return self.select_tab_by_multiple_methods(button_text, timeout)
|
||||||
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):
|
def _find_button_by_text(self, button_text, exact_match=False, timeout=10):
|
||||||
"""
|
# Этот метод больше не используется основным кодом, но оставлен для совместимости
|
||||||
Находит кнопку по тексту различными способами.
|
# или если старый метод понадобится.
|
||||||
|
pass
|
||||||
|
|
||||||
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: Поиск по тегу <button>
|
|
||||||
buttons = self.driver.find_elements(By.TAG_NAME, 'button')
|
|
||||||
for button in buttons:
|
|
||||||
if self._text_matches(button.text, button_text_lower, exact_match):
|
|
||||||
if self._is_clickable(button):
|
|
||||||
return button
|
|
||||||
|
|
||||||
# Вариант 2: Поиск по роли button
|
|
||||||
buttons = self.driver.find_elements(By.CSS_SELECTOR, '[role="button"]')
|
|
||||||
for button in buttons:
|
|
||||||
if self._text_matches(button.text, button_text_lower, exact_match):
|
|
||||||
if self._is_clickable(button):
|
|
||||||
return button
|
|
||||||
|
|
||||||
# Вариант 3: Поиск по содержимому текста (для div/span с текстом)
|
|
||||||
try:
|
|
||||||
elements = self.driver.find_elements(By.XPATH,
|
|
||||||
f"//*[contains(translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '{button_text_lower}')][not(self::script or self::style)]")
|
|
||||||
for elem in elements:
|
|
||||||
if self._is_clickable(elem):
|
|
||||||
return elem
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Вариант 4: Поиск по атрибутам (aria-label, title, etc)
|
|
||||||
for attr in ['aria-label', 'title', 'data-text', 'placeholder', 'value']:
|
|
||||||
try:
|
|
||||||
elements = self.driver.find_elements(By.XPATH, f"//*[@{attr}]")
|
|
||||||
for elem in elements:
|
|
||||||
attr_value = elem.get_attribute(attr)
|
|
||||||
if attr_value and self._text_matches(attr_value, button_text_lower, exact_match):
|
|
||||||
if self._is_clickable(elem):
|
|
||||||
return elem
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Вариант 5: Поиск label и возврат его родителя (для div+label случаев)
|
|
||||||
try:
|
|
||||||
labels = self.driver.find_elements(By.TAG_NAME, 'label')
|
|
||||||
for label in labels:
|
|
||||||
if self._text_matches(label.text, button_text_lower, exact_match):
|
|
||||||
if self._is_clickable(label):
|
|
||||||
return label
|
|
||||||
# Если сам label не кликабелен, ищем кликабельного родителя
|
|
||||||
try:
|
|
||||||
parent = label.find_element(By.XPATH, "ancestor::div[@onclick] | ancestor::div[@role='button'] | ancestor::button")
|
|
||||||
if self._is_clickable(parent):
|
|
||||||
return parent
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Вариант 6: <input> элементы (button, submit, reset)
|
|
||||||
try:
|
|
||||||
inputs = self.driver.find_elements(By.CSS_SELECTOR, 'input[type="button"], input[type="submit"], input[type="reset"]')
|
|
||||||
for inp in inputs:
|
|
||||||
value = inp.get_attribute('value')
|
|
||||||
if value and self._text_matches(value, button_text_lower, exact_match):
|
|
||||||
if self._is_clickable(inp):
|
|
||||||
return inp
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
time.sleep(0.5)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _text_matches(self, element_text, search_text, exact_match):
|
def _text_matches(self, element_text, search_text, exact_match):
|
||||||
"""Проверяет совпадение текста."""
|
"""Проверяет совпадение текста."""
|
||||||
@@ -454,182 +404,101 @@ class BrowserService:
|
|||||||
Returns:
|
Returns:
|
||||||
bool: True, если вкладка найдена и выбрана, иначе False
|
bool: True, если вкладка найдена и выбрана, иначе False
|
||||||
"""
|
"""
|
||||||
|
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()
|
tab_text_normalized = tab_text.strip()
|
||||||
tab_text_lower = tab_text_normalized.lower()
|
|
||||||
|
|
||||||
logging.info(f"🔍 Начинаю поиск вкладки: '{tab_text}'")
|
logging.info(f"🔍 Начинаю поиск вкладки: '{tab_text}' (v2)")
|
||||||
|
|
||||||
while time.time() < end_time:
|
while time.time() < end_time:
|
||||||
attempt += 1
|
attempt += 1
|
||||||
logging.info(f" Попытка {attempt}: перебираю все элементы на странице...")
|
logging.info(f" Попытка {attempt}: поиск элементов через JS...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Ищем и кликаем через JavaScript в одном скрипте
|
# Ищем и кликаем через JavaScript в одном скрипте
|
||||||
script = """
|
script = """
|
||||||
var searchText = arguments[0].toLowerCase();
|
var searchText = arguments[0].toLowerCase();
|
||||||
var allElements = document.querySelectorAll('*');
|
var allElements = document.querySelectorAll('a, div, span, li, button, td, th');
|
||||||
var foundElements = [];
|
var foundElements = [];
|
||||||
|
|
||||||
console.log('Ищу элемент с текстом:', searchText);
|
|
||||||
|
|
||||||
for (var i = 0; i < allElements.length; i++) {
|
for (var i = 0; i < allElements.length; i++) {
|
||||||
var el = allElements[i];
|
var el = allElements[i];
|
||||||
var elementText = el.textContent || el.innerText || '';
|
if (el.offsetParent === null) continue; // Skip hidden
|
||||||
var visibleText = elementText.trim();
|
|
||||||
|
|
||||||
// Проверяем только прямой текст элемента (не включая детей)
|
var elementText = el.innerText || el.textContent || '';
|
||||||
var directText = '';
|
var cleanText = elementText.trim().toLowerCase();
|
||||||
for (var j = 0; j < el.childNodes.length; j++) {
|
|
||||||
if (el.childNodes[j].nodeType === 3) { // TEXT_NODE
|
|
||||||
directText += el.childNodes[j].nodeValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
directText = directText.trim();
|
|
||||||
|
|
||||||
// Совпадение по прямому тексту или полному тексту
|
// Strict match or contains if short
|
||||||
var match = false;
|
if (cleanText === searchText || (cleanText.includes(searchText) && cleanText.length < searchText.length + 50)) {
|
||||||
if (directText.toLowerCase() === searchText ||
|
foundElements.push(el);
|
||||||
visibleText.toLowerCase() === searchText) {
|
|
||||||
match = true;
|
|
||||||
} else if (directText.toLowerCase().includes(searchText) ||
|
|
||||||
visibleText.toLowerCase().includes(searchText)) {
|
|
||||||
match = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match) {
|
|
||||||
// Проверяем видимость элемента
|
|
||||||
var rect = el.getBoundingClientRect();
|
|
||||||
var style = window.getComputedStyle(el);
|
|
||||||
|
|
||||||
if (rect.width > 0 && rect.height > 0 &&
|
|
||||||
style.display !== 'none' &&
|
|
||||||
style.visibility !== 'hidden' &&
|
|
||||||
style.opacity !== '0') {
|
|
||||||
foundElements.push({
|
|
||||||
element: el,
|
|
||||||
tag: el.tagName.toLowerCase(),
|
|
||||||
text: visibleText.substring(0, 50),
|
|
||||||
directText: directText.substring(0, 50)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return foundElements;
|
||||||
console.log('Найдено элементов:', foundElements.length);
|
|
||||||
|
|
||||||
// Пытаемся кликнуть на каждый найденный элемент
|
|
||||||
for (var k = 0; k < foundElements.length; k++) {
|
|
||||||
var item = foundElements[k];
|
|
||||||
var elem = item.element;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Прокручиваем к элементу (без анимации для скорости)
|
|
||||||
elem.scrollIntoView({block: 'center', behavior: 'auto'});
|
|
||||||
|
|
||||||
// Небольшая пауза для стабильности (выполняется синхронно)
|
|
||||||
var startWait = Date.now();
|
|
||||||
while (Date.now() - startWait < 200) {
|
|
||||||
// Ждем 200мс
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Пробую кликнуть элемент:', item.tag, item.text);
|
|
||||||
|
|
||||||
// Сначала находим САМЫЙ кликабельный элемент в иерархии
|
|
||||||
var targetToClick = elem;
|
|
||||||
|
|
||||||
// Ищем родителя который является ссылкой или кнопкой
|
|
||||||
var clickableParent = elem.closest('a, button, [role="tab"], [role="button"], li');
|
|
||||||
if (clickableParent) {
|
|
||||||
console.log('Найден кликабельный родитель:', clickableParent.tagName, clickableParent.className);
|
|
||||||
targetToClick = clickableParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Теперь пробуем все методы клика на найденном элементе
|
|
||||||
var methods = [
|
|
||||||
{
|
|
||||||
name: 'direct click',
|
|
||||||
fn: function(el) { el.click(); }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'mousedown+mouseup',
|
|
||||||
fn: function(el) {
|
|
||||||
el.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, cancelable: true}));
|
|
||||||
el.dispatchEvent(new MouseEvent('mouseup', {bubbles: true, cancelable: true}));
|
|
||||||
el.dispatchEvent(new MouseEvent('click', {bubbles: true, cancelable: true}));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'dispatchEvent click',
|
|
||||||
fn: function(el) {
|
|
||||||
el.dispatchEvent(new MouseEvent('click', {
|
|
||||||
view: window,
|
|
||||||
bubbles: true,
|
|
||||||
cancelable: true,
|
|
||||||
clientX: el.getBoundingClientRect().left + 5,
|
|
||||||
clientY: el.getBoundingClientRect().top + 5
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'focus + click',
|
|
||||||
fn: function(el) {
|
|
||||||
if (el.focus) el.focus();
|
|
||||||
el.click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// Пробуем каждый метод
|
|
||||||
for (var m = 0; m < methods.length; m++) {
|
|
||||||
try {
|
|
||||||
console.log('Пробую метод:', methods[m].name, 'на', targetToClick.tagName);
|
|
||||||
methods[m].fn(targetToClick);
|
|
||||||
console.log('✅ Успешно кликнул методом:', methods[m].name);
|
|
||||||
return {success: true, tag: targetToClick.tagName, text: item.text, method: methods[m].name};
|
|
||||||
} catch(err) {
|
|
||||||
console.log('✗ Метод', methods[m].name, 'не сработал:', err.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch(err) {
|
|
||||||
console.log('Общая ошибка при обработке элемента:', err.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {success: false, found: foundElements.length, message: 'Элементы найдены, но ни один метод клика не сработал'};
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = self.driver.execute_script(script, tab_text_lower)
|
candidates = self.driver.execute_script(script, tab_text_normalized)
|
||||||
|
|
||||||
if result and isinstance(result, dict):
|
if not candidates:
|
||||||
if result.get('success'):
|
logging.info(" ⚠️ JS поиск ничего не дал, пробую XPath...")
|
||||||
method = result.get('method', 'unknown')
|
try:
|
||||||
tag = result.get('tag', 'unknown')
|
xpath = f"//*[contains(text(), '{tab_text}')]"
|
||||||
text = result.get('text', '')
|
candidates = self.driver.find_elements(By.XPATH, xpath)
|
||||||
logging.info(f" ✅ УСПЕХ! Метод: {method}, тег: <{tag}>, текст: '{text}'")
|
except:
|
||||||
time.sleep(0.5)
|
pass
|
||||||
return True
|
|
||||||
else:
|
logging.info(f" Найдено кандидатов: {len(candidates)}")
|
||||||
found_count = result.get('found', 0)
|
|
||||||
message = result.get('message', 'Не найдено')
|
for i, element in enumerate(candidates):
|
||||||
if found_count > 0:
|
try:
|
||||||
logging.warning(f" ✗ Попытка {attempt}: найдено {found_count} элементов, но кликнуть не удалось")
|
if not element.is_displayed():
|
||||||
else:
|
continue
|
||||||
logging.warning(f" ✗ Попытка {attempt}: {message}")
|
|
||||||
else:
|
# Подсветка
|
||||||
logging.warning(f" ✗ Попытка {attempt}: некорректный результат от JavaScript")
|
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
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f" Ошибка обработки элемента: {e}")
|
||||||
|
try:
|
||||||
|
self.driver.execute_script("arguments[0].style.border='';", element)
|
||||||
|
except:
|
||||||
|
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}")
|
||||||
import traceback
|
|
||||||
logging.debug(traceback.format_exc())
|
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
logging.error(f"❌ Не удалось найти и кликнуть на вкладку '{tab_text}' за {timeout} секунд после {attempt} попыток")
|
logging.error(f"❌ Не удалось найти и кликнуть на вкладку '{tab_text}' за {timeout} секунд")
|
||||||
return False
|
return False
|
||||||
@@ -79,6 +79,15 @@ class FileProcessorService:
|
|||||||
self.browser.open_page(self.url)
|
self.browser.open_page(self.url)
|
||||||
self.browser.wait_page_load(5)
|
self.browser.wait_page_load(5)
|
||||||
|
|
||||||
|
# ПЕРВЫЙ ПРОГОН НАСТРОЙКИ: Если выбрана вкладка, пробуем её активировать на главной странице сразу
|
||||||
|
if selected_tab:
|
||||||
|
logging.info(f"🔍 [Pre-Check] Пробую найти вкладку '{selected_tab}' на стартовой странице...")
|
||||||
|
# Попытка найти вкладку с увеличенным таймаутом для первого раза
|
||||||
|
if self.browser.select_tab_by_multiple_methods(selected_tab, timeout=10):
|
||||||
|
logging.info(f"✅ [Pre-Check] Вкладка '{selected_tab}' найдена на старте.")
|
||||||
|
else:
|
||||||
|
logging.info(f"⚠️ [Pre-Check] Вкладка '{selected_tab}' пока не найдена (возможно появится после авторизации/загрузки).")
|
||||||
|
|
||||||
# Шаг 3: Обработка каждого файла
|
# Шаг 3: Обработка каждого файла
|
||||||
logging.info("\n📋 ШАГ 3: ОБРАБОТКА ФАЙЛОВ")
|
logging.info("\n📋 ШАГ 3: ОБРАБОТКА ФАЙЛОВ")
|
||||||
logging.info("-" * 70)
|
logging.info("-" * 70)
|
||||||
|
|||||||
Reference in New Issue
Block a user