Ошибка при парсинге сайта на python

Разобраться с кодом, чтобы работал корректно 24 на 7, в парсере не сильно силен, но не понимаю как исправить эту ошибку(постоянно вылетает) через рандомное время, может отработать как 2 часа, так и 3 дня без проблем, но рано или поздно вылетает. Запускаю парсер на сервере через tmux, чтобы видеть в консоли как все работает. Запустил код с бесконечным циклом запуска, но ошибка стала чаще выявляться и код все равно отваливается. Прикрепляю код:

import platform
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from bs4 import BeautifulSoup
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
import app.database.request as rq
import asyncio
import os
import time
import logging
import sys
import traceback
import random
from selenium.common.exceptions import WebDriverException, TimeoutException


# Настройка логирования
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('scraper.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:120.0) Gecko/20100101 Firefox/120.0"
]

def get_random_user_agent():
    return random.choice(USER_AGENTS)

def configure_driver_options():
    options = Options()
    
    # Обязательные настройки
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-dev-shm-usage")
    options.add_argument("--disable-gpu")
    options.add_argument("--window-size=1920,1080")
    options.add_argument("--disable-extensions")
    
    # Для Linux всегда используем headless
    if platform.system() == "Linux":
        options.add_argument("--headless=new")
    
    # Дополнительные настройки для стабильности
    options.add_argument("--disable-blink-features=AutomationControlled")
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    options.add_experimental_option('useAutomationExtension', False)
    
    return options

def create_driver():
    try:
        options = configure_driver_options()
        
        # Настройка сервиса с автоматическим управлением драйвером
        service = Service(
            ChromeDriverManager().install(),
            service_args=['--verbose'],
            log_path='chromedriver.log'
        )
        
        driver = webdriver.Chrome(service=service, options=options)
        
        # Настройки таймаутов
        driver.set_page_load_timeout(CONFIG['timeouts']['page_load'])
        driver.implicitly_wait(10)  # Базовый implicit wait
        
        # Установка случайного User-Agent
        driver.execute_cdp_cmd('Network.setUserAgentOverride', {
            "userAgent": get_random_user_agent()
        })
        
        return driver
        
    except Exception as e:
        logger.error(f"Ошибка создания драйвера: {str(e)}")
        logger.error(traceback.format_exc())
        raise

async def human_type(element, text):
    for char in text:
        element.send_keys(char)
        await asyncio.sleep(random.uniform(0.1, 0.3))

async def login(driver):
    for attempt in range(CONFIG['max_errors']['login']):
        try:
            logger.info(f"Попытка входа {attempt + 1}/{CONFIG['max_errors']['login']}")
            
            # Обновляем User-Agent
            driver.execute_cdp_cmd('Network.setUserAgentOverride', {
                "userAgent": get_random_user_agent()
            })
            
            # Загрузка страницы входа
            driver.get(CONFIG['login_url'])
            
            # Ожидание элементов формы
            WebDriverWait(driver, CONFIG['timeouts']['element_wait']).until(
                EC.presence_of_element_located((By.NAME, 'login')))
            
            # Заполнение формы
            username = driver.find_element(By.NAME, 'login')
            username.clear()
            await human_type(username, CONFIG['credentials']['username'])
            
            password = driver.find_element(By.NAME, 'pass')
            password.clear()
            await human_type(password, CONFIG['credentials']['password'])
            
            # Клик по кнопке входа
            login_button = WebDriverWait(driver, CONFIG['timeouts']['element_wait']).until(
                EC.element_to_be_clickable((By.XPATH, '//input[@type="submit" and @value="Войти"]')))
            
            await asyncio.sleep(random.uniform(0.5, 1.5))
            login_button.click()
            
            # Проверка успешного входа
            await asyncio.sleep(3)
            if "orders" in driver.current_url or "login" not in driver.current_url:
                logger.info("Авторизация успешна")
                return True
            
            logger.warning(f"Попытка входа {attempt + 1} не удалась")
            
        except Exception as e:
            logger.error(f"Ошибка при входе (попытка {attempt + 1}): {str(e)}")
            if attempt == CONFIG['max_errors']['login'] - 1:
                raise
            await asyncio.sleep(5 * (attempt + 1))
    
    return False

async def process_table(driver):
    try:
        # Ожидаем загрузку таблицы и кнопки
        WebDriverWait(driver, CONFIG['timeouts']['element_wait']).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "table.rows")))
        
        html = driver.page_source
        soup = BeautifulSoup(html, 'html.parser')
        table = soup.select_one('table.rows')
        
        if not table:
            logger.warning("Таблица не найдена")
            return False
        
        # Получаем все строки с данными
        rows = table.select('tbody tr')
        if not rows:
            logger.warning("Нет строк данных в таблице")
            return False
        
        # Обрабатываем только первую строку (самую новую заявку)
        first_row = rows[0]
        notify_button = first_row.select_one('a.notify_seen')
        
        if not notify_button:
            logger.info("Кнопка 'отметить как просмотренную' отсутствует - пропускаем")
            return False
        
        # Извлекаем данные из строки
        data = [td.get_text(strip=True) for td in first_row.select('td')]
        logger.info(f"Получены данные: {data}")
        
        try:
            # Находим и кликаем кнопку через Selenium
            button = WebDriverWait(driver, CONFIG['timeouts']['element_wait']).until(
                EC.element_to_be_clickable((By.CSS_SELECTOR, 'a.notify_seen')))
            
            driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", button)
            await asyncio.sleep(random.uniform(0.5, 1.5))  # Естественная задержка
            button.click()
            logger.info("Заявка отмечена как просмотренная")
            
            # Проверяем успешность клика (можно добавить ожидание изменения состояния)
            await asyncio.sleep(2)
            
            # Сохраняем данные в БД
            await rq.insert_data(data)
            return True
            
        except Exception as e:
            logger.error(f"Ошибка при обработке кнопки: {str(e)}")
            return False
        
    except Exception as e:
        logger.error(f"Ошибка обработки таблицы: {str(e)}")
        return False

async def scrape_data(driver):
    error_count = 0
    
    while error_count < CONFIG['max_errors']['scraping']:
        try:
            logger.info("Загрузка страницы с заявками...")
            
            # Обновляем User-Agent
            driver.execute_cdp_cmd('Network.setUserAgentOverride', {
                "userAgent": get_random_user_agent()
            })
            
            # Загрузка страницы с таймаутом
            try:
                driver.get(CONFIG['data_url'])
                WebDriverWait(driver, CONFIG['timeouts']['element_wait']).until(
                    EC.presence_of_element_located((By.CSS_SELECTOR, "table.rows")))
                
                logger.info("Таблица успешно загружена")
                error_count = 0  # Сброс счетчика ошибок при успехе
                
                # Обработка данных таблицы
                if await process_table(driver):
                    # Случайная задержка между запросами
                    delay = random.randint(*CONFIG['timeouts']['between_requests'])
                    logger.info(f"Ожидание {delay} секунд перед следующим запросом...")
                    await asyncio.sleep(delay)
                    continue
                
            except TimeoutException:
                logger.warning("Таймаут при загрузке таблицы")
                error_count += 1
                save_page_source(driver, "timeout_error")
            except WebDriverException as e:
                logger.error(f"Ошибка WebDriver: {str(e)}")
                error_count += 1
                save_page_source(driver, "webdriver_error")
            
            # Задержка перед повторной попыткой
            logger.info(f"Повторная попытка через {CONFIG['timeouts']['retry_delay']} секунд...")
            await asyncio.sleep(CONFIG['timeouts']['retry_delay'])
            
        except Exception as e:
            logger.error(f"Критическая ошибка: {str(e)}")
            error_count += 1
            if error_count >= CONFIG['max_errors']['scraping']:
                raise Exception("Превышено максимальное количество ошибок")
            
            await asyncio.sleep(CONFIG['timeouts']['retry_delay'] * 2)

def save_page_source(driver, prefix=""):
    try:
        timestamp = int(time.time())
        filename = f"debug/page_source_{prefix}_{timestamp}.html"
        os.makedirs(os.path.dirname(filename), exist_ok=True)
        
        with open(filename, "w", encoding="utf-8") as file:
            file.write(driver.page_source)
        logger.info(f"Сохранен HTML: {filename}")
    except Exception as e:
        logger.error(f"Ошибка при сохранении HTML: {str(e)}")

async def scraping_session():
    driver = None
    try:
        driver = create_driver()
        
        # Проверка работоспособности драйвера
        driver.get("about:blank")
        if "about:blank" not in driver.current_url:
            raise Exception("Драйвер не смог загрузить тестовую страницу")
        
        # Авторизация
        if not await login(driver):
            logger.error("Не удалось авторизоваться")
            return False
        
        # Сбор данных
        await scrape_data(driver)
        return True
        
    except Exception as e:
        logger.error(f"Ошибка в сессии сбора данных: {str(e)}")
        logger.error(traceback.format_exc())
        save_page_source(driver, "session_error")
        return False
    finally:
        if driver:
            try:
                driver.quit()
            except Exception as e:
                logger.error(f"Ошибка при закрытии драйвера: {str(e)}")

async def main():
    restarts = 0
    
    while restarts < CONFIG['max_errors']['restarts']:
        try:
            logger.info(f"=== Запуск программы (попытка {restarts + 1}/{CONFIG['max_errors']['restarts']}) ===")
            
            if await scraping_session():
                logger.info("Сессия успешно завершена")
                break
                
            restarts += 1
            if restarts < CONFIG['max_errors']['restarts']:
                logger.info(f"Перезапуск через {CONFIG['timeouts']['retry_delay']} секунд...")
                await asyncio.sleep(CONFIG['timeouts']['retry_delay'])
            
        except KeyboardInterrupt:
            logger.info("Программа завершена по запросу пользователя")
            break
        except Exception as e:
            logger.error(f"Критическая ошибка: {str(e)}")
            restarts += 1
            if restarts < CONFIG['max_errors']['restarts']:
                logger.info(f"Перезапуск через {CONFIG['timeouts']['retry_delay'] * 2} секунд...")
                await asyncio.sleep(CONFIG['timeouts']['retry_delay'] * 2)
            else:
                logger.error("Достигнуто максимальное количество перезапусков")

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        logger.info("Программа завершена")
        sys.exit(0)
    except Exception as e:
        logger.error(f"Фатальная ошибка: {str(e)}")
        sys.exit(1)введите сюда код

Через несколько собранных заявок вылетает ошибка:

Ошибка при обработке заявки *****(попытка 1/3): Message:
Stacktrace:
#0 0x55fa74f88ffa <unknown>
#1 0x55fa74a47970 <unknown>
#2 0x55fa74a99385 <unknown>
#3 0x55fa74a995b1 <unknown>
#4 0x55fa74ae83c4 <unknown>
#5 0x55fa74abf2bd <unknown>
#6 0x55fa74ae570c <unknown>
#7 0x55fa74abf063 <unknown>
#8 0x55fa74a8b328 <unknown>
#9 0x55fa74a8c491 <unknown>
#10 0x55fa74f5042b <unknown>
#11 0x55fa74f542ec <unknown>
#12 0x55fa74f37a22 <unknown>
#13 0x55fa74f54e64 <unknown>
#14 0x55fa74f1bbef <unknown>
#15 0x55fa74f77558 <unknown>
#16 0x55fa74f77736 <unknown>
#17 0x55fa74f87e76 <unknown>
#18 0x7fee75c62609 start_thread

- ERROR - Ошибка при входе (попытка 1): Message: timeout: Timed out receiving message from renderer: 56.926
  (Session info: chrome=134.0.6998.165)
Stacktrace:
#0 0x562c9b285ffa <unknown>
#1 0x562c9ad44970 <unknown>
#2 0x562c9ad2bd0d <unknown>
#3 0x562c9ad2b9ef <unknown>
#4 0x562c9ad29739 <unknown>
#5 0x562c9ad2a17f <unknown>
#6 0x562c9ad398bb <unknown>
#7 0x562c9ad52dbe <unknown>
#8 0x562c9ad594cb <unknown>
#9 0x562c9ad2a890 <unknown>
#10 0x562c9ad52b17 <unknown>
#11 0x562c9ade2a4a <unknown>
#12 0x562c9adbc063 <unknown>
#13 0x562c9ad88328 <unknown>
#14 0x562c9ad89491 <unknown>
#15 0x562c9b24d42b <unknown>
#16 0x562c9b2512ec <unknown>
#17 0x562c9b234a22 <unknown>
#18 0x562c9b251e64 <unknown>
#19 0x562c9b218bef <unknown>
#20 0x562c9b274558 <unknown>
#21 0x562c9b274736 <unknown>
#22 0x562c9b284e76 <unknown>
#23 0x7f57d8bfe609 start_thread

Как можно решить эту проблему?


Ответы (0 шт):