"""Stealth browser factory — bypasses Cloudflare/ArvanCloud bot detection.""" from __future__ import annotations import random import time from contextlib import contextmanager from typing import Generator import undetected_chromedriver as uc from selenium.webdriver.chrome.options import Options from selenium_stealth import stealth from config import config _USER_AGENTS = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", ] def _build_options(user_agent: str, headless: bool) -> Options: opts = Options() if headless: opts.add_argument("--headless=new") opts.add_argument(f"--user-agent={user_agent}") opts.add_argument("--no-sandbox") opts.add_argument("--disable-dev-shm-usage") opts.add_argument("--disable-blink-features=AutomationControlled") opts.add_argument("--disable-infobars") opts.add_argument("--window-size=1920,1080") opts.add_experimental_option("excludeSwitches", ["enable-automation"]) opts.add_experimental_option("useAutomationExtension", False) if config.chrome_binary: opts.binary_location = config.chrome_binary return opts def make_driver(headless: bool | None = None) -> uc.Chrome: """Return a stealthed undetected Chrome instance.""" use_headless = config.headless if headless is None else headless ua = random.choice(_USER_AGENTS) opts = _build_options(ua, use_headless) driver = uc.Chrome(options=opts, use_subprocess=True) stealth( driver, languages=["en-US", "en"], vendor="Google Inc.", platform="Win32", webgl_vendor="Intel Inc.", renderer="Intel Iris OpenGL Engine", fix_hairline=True, ) # Mask navigator.webdriver via CDP driver.execute_cdp_cmd( "Page.addScriptToEvaluateOnNewDocument", { "source": """ Object.defineProperty(navigator, 'webdriver', {get: () => undefined}); Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5]}); Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']}); """ }, ) return driver @contextmanager def driver_session(headless: bool | None = None) -> Generator[uc.Chrome, None, None]: """Context manager that guarantees driver.quit() on exit.""" driver = make_driver(headless) try: yield driver finally: driver.quit() def human_delay(lo: float = 0.8, hi: float = 2.5) -> None: """Sleep for a random human-like interval.""" time.sleep(random.uniform(lo, hi))