ff1
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
import time
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import platform
|
||||
import subprocess
|
||||
@@ -37,7 +38,16 @@ class BrowserService:
|
||||
def _find_firefox_path():
|
||||
"""Автоматически находит путь к Firefox для текущей ОС."""
|
||||
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":
|
||||
# Windows пути - разные места установки Firefox
|
||||
@@ -47,6 +57,7 @@ class BrowserService:
|
||||
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"),
|
||||
os.path.expandvars(r"%APPDATA%\Mozilla Firefox\firefox.exe"),
|
||||
]
|
||||
|
||||
# Пытаемся найти через реестр (Windows)
|
||||
@@ -72,10 +83,12 @@ class BrowserService:
|
||||
else: # Linux
|
||||
# Linux пути
|
||||
possible_paths = [
|
||||
"/snap/bin/firefox",
|
||||
"/usr/bin/firefox",
|
||||
"/usr/bin/firefox-esr",
|
||||
"/snap/bin/firefox",
|
||||
"/usr/local/bin/firefox",
|
||||
"/opt/firefox/firefox",
|
||||
"/usr/lib/firefox/firefox",
|
||||
]
|
||||
|
||||
# Ищем Firefox в возможных местах
|
||||
@@ -84,17 +97,16 @@ class BrowserService:
|
||||
logging.info(f"✅ Firefox найден: {path}")
|
||||
return path
|
||||
|
||||
# Пытаемся найти через which/where команды
|
||||
# Последний шанс: subprocess (если shutil.which пропустил что-то специфичное)
|
||||
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)
|
||||
cmd = ["where", "firefox.exe"] if system == "Windows" else ["which", "firefox"]
|
||||
result = subprocess.run(cmd, 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
|
||||
if os.path.exists(firefox_path):
|
||||
logging.info(f"✅ Firefox найден через команду search: {firefox_path}")
|
||||
return firefox_path
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -112,10 +124,6 @@ class BrowserService:
|
||||
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}")
|
||||
@@ -126,20 +134,45 @@ class BrowserService:
|
||||
logging.info("🚀 Запуск Firefox драйвера с ВИДИМЫМ окном...")
|
||||
|
||||
# Попытка 1: Использовать webdriver-manager (самый надежный способ)
|
||||
if HAS_WEBDRIVER_MANAGER:
|
||||
if HAS_WEBDRIVER_MANAGER and not self.driver:
|
||||
try:
|
||||
logging.info(" Попытка 1: webdriver-manager...")
|
||||
service = FirefoxService(GeckoDriverManager().install())
|
||||
self.driver = webdriver.Firefox(service=service, options=firefox_options)
|
||||
logging.info(" ✅ Успешно запущено через webdriver-manager")
|
||||
except Exception as e:
|
||||
logging.warning(f" webdriver-manager не сработал: {e}")
|
||||
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:
|
||||
try:
|
||||
logging.info(" Попытка 2: Прямой запуск (Selenium ищет geckodriver в PATH)...")
|
||||
logging.info(" Попытка 3: Прямой запуск (Selenium ищет geckodriver в PATH)...")
|
||||
self.driver = webdriver.Firefox(options=firefox_options)
|
||||
logging.info(" ✅ Успешно запущено через системный PATH")
|
||||
except Exception as e:
|
||||
logging.warning(f" Прямой запуск не сработал: {e}")
|
||||
self.driver = None
|
||||
@@ -154,6 +187,18 @@ class BrowserService:
|
||||
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. Скачайте 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:
|
||||
logging.error(f"❌ Ошибка при настройке драйвера: {e}")
|
||||
logging.error(f"\n Решения:")
|
||||
@@ -180,119 +225,24 @@ class BrowserService:
|
||||
|
||||
def click_button_by_text(self, button_text, exact_match=False, timeout=10):
|
||||
"""
|
||||
Ищет и нажимает кнопку по тексту в различных формах.
|
||||
Ищет и нажимает кнопку (или любой элемент) по тексту, используя продвинутый метод поиска.
|
||||
|
||||
Args:
|
||||
button_text (str): Текст кнопки для поиска
|
||||
exact_match (bool): Точное совпадение или частичное
|
||||
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
|
||||
logging.info(f"🔎 Ищу кнопку/элемент с текстом: '{button_text}' (Robust)")
|
||||
return self.select_tab_by_multiple_methods(button_text, timeout)
|
||||
|
||||
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: Поиск по тегу <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
|
||||
# Этот метод больше не используется основным кодом, но оставлен для совместимости
|
||||
# или если старый метод понадобится.
|
||||
pass
|
||||
|
||||
|
||||
def _text_matches(self, element_text, search_text, exact_match):
|
||||
"""Проверяет совпадение текста."""
|
||||
@@ -454,182 +404,101 @@ class BrowserService:
|
||||
Returns:
|
||||
bool: True, если вкладка найдена и выбрана, иначе False
|
||||
"""
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
|
||||
end_time = time.time() + timeout
|
||||
attempt = 0
|
||||
|
||||
# Нормализуем текст для поиска
|
||||
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:
|
||||
attempt += 1
|
||||
logging.info(f" Попытка {attempt}: перебираю все элементы на странице...")
|
||||
logging.info(f" Попытка {attempt}: поиск элементов через JS...")
|
||||
|
||||
try:
|
||||
# Ищем и кликаем через JavaScript в одном скрипте
|
||||
script = """
|
||||
var searchText = arguments[0].toLowerCase();
|
||||
var allElements = document.querySelectorAll('*');
|
||||
var allElements = document.querySelectorAll('a, div, span, li, button, td, th');
|
||||
var foundElements = [];
|
||||
|
||||
console.log('Ищу элемент с текстом:', searchText);
|
||||
|
||||
for (var i = 0; i < allElements.length; i++) {
|
||||
var el = allElements[i];
|
||||
var elementText = el.textContent || el.innerText || '';
|
||||
var visibleText = elementText.trim();
|
||||
if (el.offsetParent === null) continue; // Skip hidden
|
||||
|
||||
// Проверяем только прямой текст элемента (не включая детей)
|
||||
var directText = '';
|
||||
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();
|
||||
var elementText = el.innerText || el.textContent || '';
|
||||
var cleanText = elementText.trim().toLowerCase();
|
||||
|
||||
// Совпадение по прямому тексту или полному тексту
|
||||
var match = false;
|
||||
if (directText.toLowerCase() === searchText ||
|
||||
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)
|
||||
});
|
||||
}
|
||||
// Strict match or contains if short
|
||||
if (cleanText === searchText || (cleanText.includes(searchText) && cleanText.length < searchText.length + 50)) {
|
||||
foundElements.push(el);
|
||||
}
|
||||
}
|
||||
|
||||
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: 'Элементы найдены, но ни один метод клика не сработал'};
|
||||
return foundElements;
|
||||
"""
|
||||
|
||||
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 result.get('success'):
|
||||
method = result.get('method', 'unknown')
|
||||
tag = result.get('tag', 'unknown')
|
||||
text = result.get('text', '')
|
||||
logging.info(f" ✅ УСПЕХ! Метод: {method}, тег: <{tag}>, текст: '{text}'")
|
||||
time.sleep(0.5)
|
||||
return True
|
||||
else:
|
||||
found_count = result.get('found', 0)
|
||||
message = result.get('message', 'Не найдено')
|
||||
if found_count > 0:
|
||||
logging.warning(f" ✗ Попытка {attempt}: найдено {found_count} элементов, но кликнуть не удалось")
|
||||
else:
|
||||
logging.warning(f" ✗ Попытка {attempt}: {message}")
|
||||
else:
|
||||
logging.warning(f" ✗ Попытка {attempt}: некорректный результат от JavaScript")
|
||||
if not candidates:
|
||||
logging.info(" ⚠️ JS поиск ничего не дал, пробую XPath...")
|
||||
try:
|
||||
xpath = f"//*[contains(text(), '{tab_text}')]"
|
||||
candidates = self.driver.find_elements(By.XPATH, xpath)
|
||||
except:
|
||||
pass
|
||||
|
||||
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
|
||||
|
||||
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}")
|
||||
import traceback
|
||||
logging.debug(traceback.format_exc())
|
||||
time.sleep(1)
|
||||
|
||||
logging.error(f"❌ Не удалось найти и кликнуть на вкладку '{tab_text}' за {timeout} секунд после {attempt} попыток")
|
||||
logging.error(f"❌ Не удалось найти и кликнуть на вкладку '{tab_text}' за {timeout} секунд")
|
||||
return False
|
||||
@@ -79,6 +79,15 @@ class FileProcessorService:
|
||||
self.browser.open_page(self.url)
|
||||
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: Обработка каждого файла
|
||||
logging.info("\n📋 ШАГ 3: ОБРАБОТКА ФАЙЛОВ")
|
||||
logging.info("-" * 70)
|
||||
|
||||
Reference in New Issue
Block a user