Skip to main content
Glama

MCP Selenium WebDriver

by Nixon-Suarez
  • Linux
  • Apple
browser_manager.py18.4 kB
""" Módulo para gestionar instancias de WebDriver con configuraciones avanzadas. Basado en el ejemplo de DriverFactory.py del usuario. """ import os import platform import random import tempfile import shutil import time from typing import Optional, Dict, Any from selenium import webdriver from selenium.webdriver.chrome.options import Options as ChromeOptions from selenium.webdriver.firefox.options import Options as FirefoxOptions from selenium.webdriver.chrome.service import Service as ChromeService from selenium.webdriver.firefox.service import Service as FirefoxService from selenium.webdriver.remote.webdriver import WebDriver from webdriver_manager.chrome import ChromeDriverManager from webdriver_manager.firefox import GeckoDriverManager try: import undetected_chromedriver as uc UNDETECTED_CHROME_AVAILABLE = True except ImportError: UNDETECTED_CHROME_AVAILABLE = False from config import BrowserOptions, ProxyConfig, DetectionEvasionConfig class WebDriverManager: """Gestor de instancias de WebDriver con configuraciones avanzadas.""" def __init__(self): self.system = platform.system().lower() self.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 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0', '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' ] # Rutas específicas de navegadores por SO self.browser_paths = { "windows": { "chrome": [ r"C:\Program Files\Google\Chrome\Application\chrome.exe", r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" ], "firefox": [ r"C:\Program Files\Mozilla Firefox\firefox.exe", r"C:\Program Files (x86)\Mozilla Firefox\firefox.exe" ] }, "linux": { "chrome": [ "/usr/bin/google-chrome", "/usr/bin/google-chrome-stable", "/usr/bin/chromium", "/usr/bin/chromium-browser" ], "firefox": [ "/usr/bin/firefox", "/usr/bin/firefox-esr" ] }, "darwin": { # macOS "chrome": [ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" ], "firefox": [ "/Applications/Firefox.app/Contents/MacOS/firefox" ] } } def _get_browser_binary_path(self, browser_name: str) -> Optional[str]: """Detecta la ruta del binario del navegador según el SO.""" if self.system not in self.browser_paths: return None if browser_name not in self.browser_paths[self.system]: return None for path in self.browser_paths[self.system][browser_name]: if os.path.exists(path): return path return None def create_chrome_driver( self, browser_options: BrowserOptions, proxy_config: Optional[ProxyConfig] = None, detection_evasion: Optional[DetectionEvasionConfig] = None ) -> Dict[str, Any]: """Crea una instancia de Chrome WebDriver con configuraciones stealth.""" if detection_evasion and detection_evasion.use_undetected_chrome and UNDETECTED_CHROME_AVAILABLE: return self._create_undetected_chrome(browser_options, proxy_config, detection_evasion) else: return self._create_standard_chrome(browser_options, proxy_config, detection_evasion) def _create_undetected_chrome( self, browser_options: BrowserOptions, proxy_config: Optional[ProxyConfig], detection_evasion: DetectionEvasionConfig ) -> Dict[str, Any]: """Crea una instancia de Chrome usando undetected-chromedriver.""" try: # Crear driver con undetected-chromedriver (similar al ejemplo del usuario) driver = uc.Chrome( headless=browser_options.headless, version_main=None # Detectar automáticamente ) # Aplicar configuraciones stealth similares al ejemplo del usuario self._apply_stealth_configurations(driver, browser_options, detection_evasion) # Crear directorio temporal para user data user_data_dir = tempfile.mkdtemp() return { "driver": driver, "user_data_dir": user_data_dir } except Exception as e: raise Exception(f"Error al crear undetected Chrome driver: {e}") def _create_standard_chrome( self, browser_options: BrowserOptions, proxy_config: Optional[ProxyConfig], detection_evasion: Optional[DetectionEvasionConfig] ) -> Dict[str, Any]: """Crea una instancia de Chrome estándar con configuraciones stealth.""" try: options = ChromeOptions() # Detectar ruta del binario de Chrome chrome_binary = self._get_browser_binary_path("chrome") if chrome_binary: options.binary_location = chrome_binary # Crear directorio temporal único para user data con timestamp import time timestamp = str(int(time.time() * 1000000)) # microsegundos para mayor unicidad user_data_dir = tempfile.mkdtemp(prefix=f"chrome_profile_{timestamp}_") # Configuraciones básicas (sin user-data-dir para evitar conflictos) # options.add_argument(f"--user-data-dir={user_data_dir}") # Comentado temporalmente options.add_argument("--no-sandbox") options.add_argument("--disable-dev-shm-usage") options.add_argument("--disable-gpu") options.add_argument("--disable-blink-features=AutomationControlled") # Modo headless if browser_options.headless: options.add_argument("--headless=new") # Modo incógnito if browser_options.incognito: options.add_argument("--incognito") # User agent aleatorio user_agent = browser_options.user_agent if detection_evasion and detection_evasion.randomize_user_agent: user_agent = random.choice(self.user_agents) if user_agent: options.add_argument(f"--user-agent={user_agent}") # Configuraciones de contenido prefs = { "profile.default_content_setting_values.notifications": 2, "profile.default_content_settings.popups": 0 } if browser_options.disable_images: prefs["profile.managed_default_content_settings.images"] = 2 if browser_options.disable_javascript: prefs["profile.managed_default_content_settings.javascript"] = 2 options.add_experimental_option("prefs", prefs) # Configuraciones de evasión de detección options.add_experimental_option("excludeSwitches", ["enable-automation"]) options.add_experimental_option("useAutomationExtension", False) # Argumentos personalizados if browser_options.custom_args: for arg in browser_options.custom_args: options.add_argument(arg) # Configuración de proxy if proxy_config: if proxy_config.http: options.add_argument(f"--proxy-server={proxy_config.http}") elif proxy_config.https: options.add_argument(f"--proxy-server={proxy_config.https}") elif proxy_config.socks: options.add_argument(f"--proxy-server={proxy_config.socks}") # Configurar servicio service = ChromeService(ChromeDriverManager().install()) # Crear driver driver = webdriver.Chrome(service=service, options=options) # Aplicar configuraciones stealth post-creación self._apply_stealth_configurations(driver, browser_options, detection_evasion) return { "driver": driver, "user_data_dir": user_data_dir } except Exception as e: # Limpiar directorio temporal en caso de error if 'user_data_dir' in locals() and os.path.exists(user_data_dir): try: shutil.rmtree(user_data_dir) except: pass raise Exception(f"Error al crear Chrome driver estándar: {e}") def _apply_stealth_configurations( self, driver: WebDriver, browser_options: BrowserOptions, detection_evasion: Optional[DetectionEvasionConfig] ): """Aplica configuraciones stealth al driver (basado en el ejemplo del usuario).""" try: # Modificar navigator.webdriver flag (del ejemplo del usuario) driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") # Configurar viewport aleatorio pero realista viewport_width = browser_options.window_width or random.randint(1024, 1920) viewport_height = browser_options.window_height or random.randint(768, 1080) if detection_evasion and detection_evasion.randomize_viewport: viewport_width += random.randint(-100, 100) viewport_height += random.randint(-50, 50) driver.set_window_size(viewport_width, viewport_height) # Configurar user agent con CDP (del ejemplo del usuario) if detection_evasion and detection_evasion.randomize_user_agent: user_agent = random.choice(self.user_agents) try: driver.execute_cdp_cmd('Network.setUserAgentOverride', { "userAgent": user_agent }) except: pass # Fallback si CDP no está disponible # Configurar headers realistas (del ejemplo del usuario) try: driver.execute_cdp_cmd('Network.setExtraHTTPHeaders', { 'headers': { 'Accept-Language': 'es-ES,es;q=0.9,en;q=0.8', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120"', 'sec-ch-ua-platform': '"Windows"', 'sec-fetch-dest': 'document', 'sec-fetch-mode': 'navigate', 'sec-fetch-site': 'none', 'sec-fetch-user': '?1', 'upgrade-insecure-requests': '1' } }) except: pass # Fallback si CDP no está disponible # Emular características del dispositivo (del ejemplo del usuario) try: driver.execute_cdp_cmd('Emulation.setDeviceMetricsOverride', { 'mobile': False, 'width': viewport_width, 'height': viewport_height, 'deviceScaleFactor': random.choice([1, 1.25, 1.5, 2]), 'screenWidth': viewport_width, 'screenHeight': viewport_height, }) except: pass # Fallback si CDP no está disponible except Exception as e: print(f"Advertencia: No se pudieron aplicar todas las configuraciones stealth: {e}") def create_firefox_driver( self, browser_options: BrowserOptions, proxy_config: Optional[ProxyConfig] = None, detection_evasion: Optional[DetectionEvasionConfig] = None ) -> Dict[str, Any]: """Crea una instancia de Firefox WebDriver.""" try: options = FirefoxOptions() # Detectar ruta del binario de Firefox firefox_binary = self._get_browser_binary_path("firefox") if firefox_binary: options.binary_location = firefox_binary # Modo headless if browser_options.headless: options.add_argument("--headless") # User agent user_agent = browser_options.user_agent if detection_evasion and detection_evasion.randomize_user_agent: user_agent = random.choice(self.user_agents) if user_agent: options.set_preference("general.useragent.override", user_agent) # Configuraciones de contenido if browser_options.disable_images: options.set_preference("permissions.default.image", 2) if browser_options.disable_javascript: options.set_preference("javascript.enabled", False) # Configuraciones de privacidad if browser_options.incognito: options.add_argument("--private") # Configuración de proxy if proxy_config: if proxy_config.http: proxy_parts = proxy_config.http.replace("http://", "").split(":") if len(proxy_parts) == 2: options.set_preference("network.proxy.type", 1) options.set_preference("network.proxy.http", proxy_parts[0]) options.set_preference("network.proxy.http_port", int(proxy_parts[1])) options.set_preference("network.proxy.ssl", proxy_parts[0]) options.set_preference("network.proxy.ssl_port", int(proxy_parts[1])) # Configurar servicio service = FirefoxService(GeckoDriverManager().install()) # Crear driver driver = webdriver.Firefox(service=service, options=options) # Configurar tamaño de ventana if not browser_options.headless: width = browser_options.window_width or 1280 height = browser_options.window_height or 720 if detection_evasion and detection_evasion.randomize_viewport: width += random.randint(-100, 100) height += random.randint(-50, 50) driver.set_window_size(width, height) return { "driver": driver, "user_data_dir": None } except Exception as e: raise Exception(f"Error al crear Firefox driver: {e}") def create_driver( self, browser_type: str, browser_options: BrowserOptions, proxy_config: Optional[ProxyConfig] = None, detection_evasion: Optional[DetectionEvasionConfig] = None ) -> Dict[str, Any]: """Crea una instancia de WebDriver según el tipo de navegador especificado.""" browser_type = browser_type.lower() if browser_type == "chrome": return self.create_chrome_driver(browser_options, proxy_config, detection_evasion) elif browser_type == "firefox": return self.create_firefox_driver(browser_options, proxy_config, detection_evasion) else: raise ValueError(f"Tipo de navegador no soportado: {browser_type}") def login_with_retry(self, driver: WebDriver, url: str, max_retries: int = 3) -> bool: """Función de navegación con reintentos y comportamiento humano (del ejemplo del usuario).""" for attempt in range(max_retries): try: # Simular movimiento de mouse antes de cargar la página driver.execute_script(""" let event = new MouseEvent('mousemove', { 'view': window, 'bubbles': true, 'cancelable': true, 'clientX': Math.random() * window.innerWidth, 'clientY': Math.random() * window.innerHeight }); document.dispatchEvent(event); """) # Agregar delay aleatorio antes de cargar la página time.sleep(random.uniform(2, 5)) driver.get(url) # Simular scroll aleatorio scroll_amount = random.randint(100, 300) driver.execute_script(f"window.scrollBy(0, {scroll_amount})") time.sleep(random.uniform(1, 3)) return True except Exception as e: print(f"Intento {attempt + 1} fallido: {str(e)}") if attempt < max_retries - 1: # Espera exponencial entre reintentos time.sleep((attempt + 1) * random.uniform(5, 10)) continue return False return False

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Nixon-Suarez/MCP-Selenium-WebDriver'

If you have feedback or need assistance with the MCP directory API, please join our Discord server