From 7614ca495d8a52a2100516c0fcad079e191c13f4 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 17 Jul 2026 16:57:15 -0400 Subject: [PATCH 1/7] Fix WebRTC leak for non-proxied navigation --- seleniumbase/core/browser_launcher.py | 9 ++++----- seleniumbase/undetected/cdp_driver/config.py | 11 +++++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 2b39b66d3ce..f1b40593041 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -2410,11 +2410,10 @@ def _set_chrome_options( } app_state = "printing.print_preview_sticky_settings.appState" prefs[app_state] = json.dumps(pdf_settings) - if proxy_string or proxy_pac_url: - # Implementation of https://stackoverflow.com/q/65705775/7058266 - prefs["webrtc.ip_handling_policy"] = "disable_non_proxied_udp" - prefs["webrtc.multiple_routes_enabled"] = False - prefs["webrtc.nonproxied_udp_enabled"] = False + # Implementation of https://stackoverflow.com/q/65705775/7058266 + prefs["webrtc.ip_handling_policy"] = "disable_non_proxied_udp" + prefs["webrtc.multiple_routes_enabled"] = False + prefs["webrtc.nonproxied_udp_enabled"] = False chrome_options.add_experimental_option("prefs", prefs) if enable_sync: chrome_options.add_experimental_option( diff --git a/seleniumbase/undetected/cdp_driver/config.py b/seleniumbase/undetected/cdp_driver/config.py index 58f190b96e5..a5e28702fd4 100644 --- a/seleniumbase/undetected/cdp_driver/config.py +++ b/seleniumbase/undetected/cdp_driver/config.py @@ -424,12 +424,11 @@ def get_default_preferences(proxy=None): "credit_card_enabled": False } } - if proxy: - prefs["webrtc"] = { - "ip_handling_policy": "disable_non_proxied_udp", - "multiple_routes_enabled": False, - "nonproxied_udp_enabled": False - } + prefs["webrtc"] = { + "ip_handling_policy": "disable_non_proxied_udp", + "multiple_routes_enabled": False, + "nonproxied_udp_enabled": False, + } return prefs From 92c528ae17d0e07294671d09cb253c5e5ebbb8ca Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 17 Jul 2026 17:06:11 -0400 Subject: [PATCH 2/7] Drop support for the deprecated Atlas Browser --- README.md | 1 - help_docs/customizing_test_runs.md | 1 - pyproject.toml | 1 - seleniumbase/behave/behave_sb.py | 2 +- seleniumbase/console_scripts/sb_mkrec.py | 5 --- seleniumbase/console_scripts/sb_recorder.py | 15 ------- seleniumbase/core/browser_launcher.py | 17 +------ seleniumbase/core/detect_b_ver.py | 45 ------------------- .../drivers/atlas_drivers/__init__.py | 0 seleniumbase/fixtures/constants.py | 9 ---- seleniumbase/fixtures/page_utils.py | 2 +- seleniumbase/plugins/driver_manager.py | 26 +---------- seleniumbase/plugins/pytest_plugin.py | 45 ++----------------- seleniumbase/plugins/sb_manager.py | 24 +--------- seleniumbase/plugins/selenium_plugin.py | 30 +------------ .../undetected/cdp_driver/cdp_util.py | 6 +-- seleniumbase/undetected/cdp_driver/tab.py | 2 +- setup.py | 1 - 18 files changed, 13 insertions(+), 219 deletions(-) delete mode 100644 seleniumbase/drivers/atlas_drivers/__init__.py diff --git a/README.md b/README.md index 4255d326530..74bd5a5e990 100755 --- a/README.md +++ b/README.md @@ -762,7 +762,6 @@ pytest test_coffee_cart.py --trace --opera # (Shortcut for "--browser=opera".) --brave # (Shortcut for "--browser=brave".) --comet # (Shortcut for "--browser=comet".) ---atlas # (Shortcut for "--browser=atlas".) --chromium # (Shortcut for using base `Chromium`) --settings-file=FILE # (Override default SeleniumBase settings.) --env=ENV # (Set the test env. Access with "self.env" in tests.) diff --git a/help_docs/customizing_test_runs.md b/help_docs/customizing_test_runs.md index 45eb48df9e3..7a7b3d999c4 100644 --- a/help_docs/customizing_test_runs.md +++ b/help_docs/customizing_test_runs.md @@ -135,7 +135,6 @@ pytest my_first_test.py --settings-file=custom_settings.py --opera # (Shortcut for "--browser=opera".) --brave # (Shortcut for "--browser=brave".) --comet # (Shortcut for "--browser=comet".) ---atlas # (Shortcut for "--browser=atlas".) --chromium # (Shortcut for using base `Chromium`) --settings-file=FILE # (Override default SeleniumBase settings.) --env=ENV # (Set the test env. Access with "self.env" in tests.) diff --git a/pyproject.toml b/pyproject.toml index c6dd5408a84..9052279d869 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,6 @@ packages = [ "seleniumbase.drivers.opera_drivers", "seleniumbase.drivers.brave_drivers", "seleniumbase.drivers.comet_drivers", - "seleniumbase.drivers.atlas_drivers", "seleniumbase.drivers.chromium_drivers", "seleniumbase.extensions", "seleniumbase.fixtures", diff --git a/seleniumbase/behave/behave_sb.py b/seleniumbase/behave/behave_sb.py index c7f9aa8f3a3..c58f654ac33 100644 --- a/seleniumbase/behave/behave_sb.py +++ b/seleniumbase/behave/behave_sb.py @@ -894,7 +894,7 @@ def get_configured_sb(context): "\nOnly ONE default browser is allowed!\n" "%s browsers were selected: %s" % (len(browsers), browsers) ) - if sb.browser in ["opera", "brave", "comet", "atlas"]: + if sb.browser in ["opera", "brave", "comet"]: bin_loc = detect_b_ver.get_binary_location(sb.browser) if bin_loc and os.path.exists(bin_loc): sb_config._cdp_browser = sb.browser diff --git a/seleniumbase/console_scripts/sb_mkrec.py b/seleniumbase/console_scripts/sb_mkrec.py index eda1e631607..1b265ee00a6 100644 --- a/seleniumbase/console_scripts/sb_mkrec.py +++ b/seleniumbase/console_scripts/sb_mkrec.py @@ -87,7 +87,6 @@ def main(): use_opera = False use_brave = False use_comet = False - use_atlas = False use_chromium = False use_uc = False esc_end = False @@ -151,8 +150,6 @@ def main(): use_brave = True elif option.lower() == "--comet": use_comet = True - elif option.lower() == "--atlas": - use_atlas = True elif option.lower() == "--use-chromium": use_chromium = True elif option.lower() == "--ee": @@ -302,8 +299,6 @@ def main(): run_cmd += " --brave" elif use_comet: run_cmd += " --comet" - elif use_atlas: - run_cmd += " --atlas" elif use_chromium: run_cmd += " --use-chromium" if force_gui: diff --git a/seleniumbase/console_scripts/sb_recorder.py b/seleniumbase/console_scripts/sb_recorder.py index ee32e6c3838..43f9680119e 100644 --- a/seleniumbase/console_scripts/sb_recorder.py +++ b/seleniumbase/console_scripts/sb_recorder.py @@ -166,8 +166,6 @@ def do_recording( command += " --brave" elif "comet" in brx.lower(): command += " --comet" - elif "atlas" in brx.lower(): - command += " --atlas" elif "chromium" in brx.lower(): command += " --use-chromium" if ucb: @@ -213,8 +211,6 @@ def do_playback(file_name, brx, window, demo_mode=False): command += " --brave" elif "comet" in brx.lower(): command += " --comet" - elif "atlas" in brx.lower(): - command += " --atlas" elif "chromium" in brx.lower(): command += " --use-chromium" if demo_mode: @@ -265,7 +261,6 @@ def create_tkinter_gui(): use_behave = False use_sb_mgr = False use_sb_cdp = False - use_atlas = False command_args = sys.argv[2:] if ( "--uc" in command_args @@ -285,8 +280,6 @@ def create_tkinter_gui(): use_sb_mgr = True if "--rec-sb-cdp" in command_args: use_sb_cdp = True - if "--atlas" in command_args: - use_atlas = True tk.Label(window, text="\nSelect a web browser to use:").pack() br_count = 2 @@ -318,12 +311,6 @@ def create_tkinter_gui(): options_list.append("Comet Browser") br_order["comet"] = br_count br_count += 1 - if use_atlas: - with suppress(Exception): - if os.path.exists(detect_b_ver.get_binary_location("atlas")): - options_list.append("Atlas Browser") - br_order["atlas"] = br_count - br_count += 1 brx = tk.StringVar(window) if "--use-chromium" in command_args or "--chromium" in command_args: brx.set(options_list[1]) @@ -339,8 +326,6 @@ def create_tkinter_gui(): brx.set(options_list[br_order["brave"]]) elif "--comet" in command_args and "comet" in br_order: brx.set(options_list[br_order["comet"]]) - elif "--atlas" in command_args and "atlas" in br_order: - brx.set(options_list[br_order["atlas"]]) else: brx.set(options_list[0]) question_menu = tk.OptionMenu(window, brx, *options_list) diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index f1b40593041..a9e8616fef2 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -27,7 +27,6 @@ from seleniumbase.drivers import opera_drivers # still uses chromedriver from seleniumbase.drivers import brave_drivers # still uses chromedriver from seleniumbase.drivers import comet_drivers # still uses chromedriver -from seleniumbase.drivers import atlas_drivers # still uses chromedriver from seleniumbase.drivers import chromium_drivers # still uses chromedriver from seleniumbase import extensions # browser extensions storage folder from seleniumbase.config import settings @@ -48,7 +47,6 @@ DRIVER_DIR_OPERA = os.path.dirname(os.path.realpath(opera_drivers.__file__)) DRIVER_DIR_BRAVE = os.path.dirname(os.path.realpath(brave_drivers.__file__)) DRIVER_DIR_COMET = os.path.dirname(os.path.realpath(comet_drivers.__file__)) -DRIVER_DIR_ATLAS = os.path.dirname(os.path.realpath(atlas_drivers.__file__)) DRIVER_DIR_CHROMIUM = os.path.dirname( os.path.realpath(chromium_drivers.__file__) ) @@ -590,7 +588,7 @@ def uc_open_with_reconnect(driver, url, reconnect_time=None): """Open a url, disconnect chromedriver, wait, and reconnect.""" if ( hasattr(sb_config, "_cdp_browser") - and sb_config._cdp_browser in ["comet", "opera", "atlas"] + and sb_config._cdp_browser in ["comet", "opera"] ): if not __is_cdp_swap_needed(driver): if not driver.current_url.startswith( @@ -2511,8 +2509,6 @@ def _set_chrome_options( and ( binary_location.lower().endswith("comet") or binary_location.lower().endswith("comet.exe") - or binary_location.lower().endswith("atlas") - or binary_location.lower().endswith("atlas.exe") ) ): # AI browsers don't like Incognito / Guest Mode @@ -3115,9 +3111,6 @@ def get_driver( elif _special_binary_exists(binary_location, "comet"): driver_dir = DRIVER_DIR_COMET sb_config._cdp_browser = "comet" - elif _special_binary_exists(binary_location, "atlas"): - driver_dir = DRIVER_DIR_ATLAS - sb_config._cdp_browser = "atlas" if ( hasattr(sb_config, "settings") and getattr(sb_config.settings, "NEW_DRIVER_DIR", None) @@ -4134,9 +4127,6 @@ def get_local_driver( elif _special_binary_exists(binary_location, "comet"): special_chrome = True driver_dir = DRIVER_DIR_COMET - elif _special_binary_exists(binary_location, "atlas"): - special_chrome = True - driver_dir = DRIVER_DIR_ATLAS if ( hasattr(sb_config, "settings") and getattr(sb_config.settings, "NEW_DRIVER_DIR", None) @@ -4972,11 +4962,6 @@ def get_local_driver( local_chromedriver = DRIVER_DIR_COMET + "/chromedriver" if IS_WINDOWS: local_chromedriver = DRIVER_DIR_COMET + "/chromedriver.exe" - if _special_binary_exists(binary_location, "atlas"): - set_chromium = "atlas" - local_chromedriver = DRIVER_DIR_ATLAS + "/chromedriver" - if IS_WINDOWS: - local_chromedriver = DRIVER_DIR_ATLAS + "/chromedriver.exe" try: chrome_options = _set_chrome_options( browser_name, diff --git a/seleniumbase/core/detect_b_ver.py b/seleniumbase/core/detect_b_ver.py index b6590c1e247..4966a79b29b 100644 --- a/seleniumbase/core/detect_b_ver.py +++ b/seleniumbase/core/detect_b_ver.py @@ -43,7 +43,6 @@ class ChromeType(object): OPERA = "opera" BRAVE = "brave" COMET = "comet" - ATLAS = "atlas" PATTERN = { @@ -225,14 +224,6 @@ def comet_on_linux_path(browser_type=None): return "" # Comet Browser isn't supported on Linux yet -def atlas_on_linux_path(browser_type=None): - if browser_type and browser_type != ChromeType.ATLAS: - return "" - if os_name() != OSType.LINUX: - return "" - return "" # Atlas Browser isn't supported on Linux yet - - def chrome_on_windows_path(browser_type=None): if browser_type and browser_type != ChromeType.GOOGLE: return "" @@ -381,36 +372,6 @@ def comet_on_windows_path(browser_type=None): return "" -def atlas_on_windows_path(browser_type=None): - if browser_type and browser_type != ChromeType.ATLAS: - return "" - if os_name() != OSType.WIN: - return "" - candidates = [] - for item in map( - os.environ.get, - ( - "LOCALAPPDATA", - "PROGRAMFILES", - "PROGRAMFILES(X86)", - "PROGRAMW6432", - ), - ): - for subitem in ( - "OpenAI/Atlas/Application", - "Atlas/Application", - "Programs/Atlas", - ): - try: - candidates.append(os.sep.join((item, subitem, "atlas.exe"))) - except TypeError: - pass - for candidate in candidates: - if os.path.exists(candidate) and os.access(candidate, os.X_OK): - return os.path.normpath(candidate) - return "" - - def windows_browser_apps_to_cmd(*apps): """Create analogue of browser --version command for windows.""" powershell = determine_powershell() @@ -460,12 +421,6 @@ def get_binary_location(browser_type, chromium_ok=False): r"/Contents/MacOS/Comet", OSType.WIN: comet_on_windows_path(browser_type), }, - ChromeType.ATLAS: { - OSType.LINUX: atlas_on_linux_path(browser_type), - OSType.MAC: r"/Applications/ChatGPT Atlas.app" - r"/Contents/MacOS/ChatGPT Atlas", - OSType.WIN: atlas_on_windows_path(browser_type), - }, } return cmd_mapping[browser_type][os_name()] diff --git a/seleniumbase/drivers/atlas_drivers/__init__.py b/seleniumbase/drivers/atlas_drivers/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/seleniumbase/fixtures/constants.py b/seleniumbase/fixtures/constants.py index 061fd2c0f18..c45b5b3ab62 100644 --- a/seleniumbase/fixtures/constants.py +++ b/seleniumbase/fixtures/constants.py @@ -382,7 +382,6 @@ class ValidBrowsers: "opera", "brave", "comet", - "atlas", ] @@ -392,7 +391,6 @@ class ChromiumSubs: "opera", "brave", "comet", - "atlas", ] @@ -415,9 +413,6 @@ class ValidBinaries: "comet", "comet-browser", "comet-stable", - "atlas", - "atlas-browser", - "atlas-stable", "chrome.exe", # WSL (Windows Subsystem for Linux) "chromium.exe", # WSL (Windows Subsystem for Linux) ] @@ -441,9 +436,6 @@ class ValidBinaries: "Opera", "Comet Browser", "Comet", - "ChatGPT Atlas", - "Atlas Browser", - "Atlas", ] valid_edge_binaries_on_macos = [ "Microsoft Edge", @@ -455,7 +447,6 @@ class ValidBinaries: "brave.exe", "opera.exe", "comet.exe", - "atlas.exe", ] valid_edge_binaries_on_windows = [ "msedge.exe", diff --git a/seleniumbase/fixtures/page_utils.py b/seleniumbase/fixtures/page_utils.py index a659b3f6e0c..55fc005c8af 100644 --- a/seleniumbase/fixtures/page_utils.py +++ b/seleniumbase/fixtures/page_utils.py @@ -112,7 +112,7 @@ def looks_like_a_page_url(url): "http:", "https:", "://", "about:", "blob:", "chrome:", "opera:", "data:", "edge:", "file:", "view-source:", "chrome-search:", "chrome-extension:", "chrome-untrusted:", "isolated-app:", - "chrome-devtools:", "devtools:", "brave:", "comet:", "atlas:" + "chrome-devtools:", "devtools:", "brave:", "comet:" )) diff --git a/seleniumbase/plugins/driver_manager.py b/seleniumbase/plugins/driver_manager.py index 42f5dc762ff..4be5af844af 100644 --- a/seleniumbase/plugins/driver_manager.py +++ b/seleniumbase/plugins/driver_manager.py @@ -356,16 +356,6 @@ def Driver( sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=comet") - if "--browser=atlas" in sys_argv or "--browser atlas" in sys_argv: - if not bin_loc_in_options: - bin_loc = detect_b_ver.get_binary_location("atlas") - if os.path.exists(bin_loc): - browser_changes += 1 - browser_set = "atlas" - sb_config._browser_shortcut = "atlas" - sb_config._cdp_browser = "atlas" - sb_config._cdp_bin_loc = bin_loc - browser_list.append("--browser=atlas") browser_text = browser_set if "--chrome" in sys_argv and not browser_set == "chrome": browser_changes += 1 @@ -417,16 +407,6 @@ def Driver( sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--comet") - if "--atlas" in sys_argv and not browser_set == "atlas": - if not bin_loc_in_options: - bin_loc = detect_b_ver.get_binary_location("atlas") - if os.path.exists(bin_loc): - browser_changes += 1 - browser_text = "atlas" - sb_config._browser_shortcut = "atlas" - sb_config._cdp_browser = "atlas" - sb_config._cdp_bin_loc = bin_loc - browser_list.append("--atlas") if browser_changes > 1: message = "\n\n TOO MANY browser types were entered!" message += "\n There were %s found:\n > %s" % ( @@ -748,7 +728,7 @@ def Driver( uc_cdp_events = False if ( undetectable - and browser not in ["chrome", "opera", "brave", "comet", "atlas"] + and browser not in ["chrome", "opera", "brave", "comet"] ): message = ( '\n Undetected-Chromedriver Mode ONLY supports Chromium browsers!' @@ -782,9 +762,7 @@ def Driver( if headless2 and browser == "firefox": headless2 = False # Only for Chromium browsers headless = True # Firefox has regular headless - elif browser not in [ - "chrome", "edge", "opera", "brave", "comet", "atlas" - ]: + elif browser not in ["chrome", "edge", "opera", "brave", "comet"]: headless2 = False # Only for Chromium browsers if disable_csp is None: if ( diff --git a/seleniumbase/plugins/pytest_plugin.py b/seleniumbase/plugins/pytest_plugin.py index f0670e888cb..ed46083f94b 100644 --- a/seleniumbase/plugins/pytest_plugin.py +++ b/seleniumbase/plugins/pytest_plugin.py @@ -32,7 +32,6 @@ def pytest_addoption(parser): --opera (Shortcut for "--browser=opera".) --brave (Shortcut for "--browser=brave".) --comet (Shortcut for "--browser=comet".) - --atlas (Shortcut for "--browser=atlas".) --chromium (Shortcut for using base `Chromium`) --cft (Shortcut for using `Chrome for Testing`) --chs (Shortcut for using `Chrome-Headless-Shell`) @@ -214,13 +213,6 @@ def pytest_addoption(parser): default=False, help="""Shortcut for --browser=comet""", ) - parser.addoption( - "--atlas", - action="store_true", - dest="use_atlas", - default=False, - help="""Shortcut for --browser=atlas""", - ) parser.addoption( "--use-chromium", "--chromium", @@ -1555,16 +1547,6 @@ def pytest_addoption(parser): sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=comet") - if "--browser=atlas" in sys_argv or "--browser atlas" in sys_argv: - if not bin_loc_in_options: - bin_loc = detect_b_ver.get_binary_location("atlas") - if os.path.exists(bin_loc): - browser_changes += 1 - browser_set = "atlas" - sb_config._browser_shortcut = "atlas" - sb_config._cdp_browser = "atlas" - sb_config._cdp_bin_loc = bin_loc - browser_list.append("--browser=atlas") browser_text = browser_set if "--chrome" in sys_argv and not browser_set == "chrome": browser_changes += 1 @@ -1621,16 +1603,6 @@ def pytest_addoption(parser): sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--comet") - if "--atlas" in sys_argv and not browser_set == "atlas": - if not bin_loc_in_options: - bin_loc = detect_b_ver.get_binary_location("atlas") - if os.path.exists(bin_loc): - browser_changes += 1 - browser_text = "atlas" - sb_config._browser_shortcut = "atlas" - sb_config._cdp_browser = "atlas" - sb_config._cdp_bin_loc = bin_loc - browser_list.append("--atlas") if browser_changes > 1: message = "\n TOO MANY browser types were entered!" message += "\n There were %s found:\n > %s" % ( @@ -1644,7 +1616,7 @@ def pytest_addoption(parser): using_recorder and browser_changes == 1 and browser_text not in [ - "chrome", "edge", "opera", "brave", "comet", "atlas", "chromium" + "chrome", "edge", "opera", "brave", "comet", "chromium" ] ): message = ( @@ -1668,7 +1640,7 @@ def pytest_addoption(parser): if ( browser_changes == 1 and browser_text not in [ - "chrome", "opera", "brave", "comet", "atlas", "chromium" + "chrome", "opera", "brave", "comet", "chromium" ] and undetectable ): @@ -1710,10 +1682,6 @@ def pytest_configure(config): bin_loc = detect_b_ver.get_binary_location("comet") if bin_loc and os.path.exists(bin_loc): sb_config.browser = "comet" - elif config.getoption("use_atlas"): - bin_loc = detect_b_ver.get_binary_location("atlas") - if bin_loc and os.path.exists(bin_loc): - sb_config.browser = "atlas" sb_config.account = config.getoption("account") sb_config.data = config.getoption("data") sb_config.var1 = config.getoption("var1") @@ -1735,7 +1703,7 @@ def pytest_configure(config): sb_config.headless = True # Firefox has regular headless elif ( sb_config.browser not in [ - "chrome", "edge", "opera", "brave", "comet", "atlas", "chromium" + "chrome", "edge", "opera", "brave", "comet", "chromium" ] ): sb_config.headless2 = False # Only for Chromium browsers @@ -1776,13 +1744,6 @@ def pytest_configure(config): bin_loc = detect_b_ver.get_binary_location("comet") if bin_loc and os.path.exists(bin_loc): sb_config.binary_location = bin_loc - elif ( - config.getoption("use_atlas") - or sb_config._browser_shortcut == "atlas" - ): - bin_loc = detect_b_ver.get_binary_location("atlas") - if bin_loc and os.path.exists(bin_loc): - sb_config.binary_location = bin_loc if config.getoption("use_chromium") and not sb_config.binary_location: sb_config.binary_location = "_chromium_" elif config.getoption("use_cft") and not sb_config.binary_location: diff --git a/seleniumbase/plugins/sb_manager.py b/seleniumbase/plugins/sb_manager.py index 6dfacfeeba2..15462c00c7c 100644 --- a/seleniumbase/plugins/sb_manager.py +++ b/seleniumbase/plugins/sb_manager.py @@ -424,16 +424,6 @@ def SB( sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=comet") - if "--browser=atlas" in sys_argv or "--browser atlas" in sys_argv: - if not bin_loc_in_options: - bin_loc = detect_b_ver.get_binary_location("atlas") - if os.path.exists(bin_loc): - browser_changes += 1 - browser_set = "atlas" - sb_config._browser_shortcut = "atlas" - sb_config._cdp_browser = "atlas" - sb_config._cdp_bin_loc = bin_loc - browser_list.append("--browser=atlas") browser_text = browser_set if "--chrome" in sys_argv and not browser_set == "chrome": browser_changes += 1 @@ -490,16 +480,6 @@ def SB( sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--comet") - if "--atlas" in sys_argv and not browser_set == "atlas": - if not bin_loc_in_options: - bin_loc = detect_b_ver.get_binary_location("atlas") - if os.path.exists(bin_loc): - browser_changes += 1 - browser_text = "atlas" - sb_config._browser_shortcut = "atlas" - sb_config._cdp_browser = "atlas" - sb_config._cdp_bin_loc = bin_loc - browser_list.append("--atlas") if browser_changes > 1: message = "\n\n TOO MANY browser types were entered!" message += "\n There were %s found:\n > %s" % ( @@ -821,7 +801,7 @@ def SB( uc_cdp_events = False if ( undetectable - and browser not in ["chrome", "opera", "brave", "comet", "atlas"] + and browser not in ["chrome", "opera", "brave", "comet"] ): message = ( '\n Undetected-Chromedriver Mode ONLY supports Chromium browsers!' @@ -850,7 +830,7 @@ def SB( if headless2 and browser == "firefox": headless2 = False # Only for Chromium browsers headless = True # Firefox has regular headless - elif browser not in ["chrome", "edge", "opera", "brave", "comet", "atlas"]: + elif browser not in ["chrome", "edge", "opera", "brave", "comet"]: headless2 = False # Only for Chromium browsers if not headless and not headless2: headed = True diff --git a/seleniumbase/plugins/selenium_plugin.py b/seleniumbase/plugins/selenium_plugin.py index 744d58aaa88..1aae789d78d 100644 --- a/seleniumbase/plugins/selenium_plugin.py +++ b/seleniumbase/plugins/selenium_plugin.py @@ -21,7 +21,6 @@ class SeleniumBrowser(Plugin): --opera (Shortcut for "--browser=opera".) --brave (Shortcut for "--browser=brave".) --comet (Shortcut for "--browser=comet".) - --atlas (Shortcut for "--browser=atlas".) --chromium (Shortcut for using base `Chromium`) --cft (Shortcut for using `Chrome for Testing`) --chs (Shortcut for using `Chrome-Headless-Shell`) @@ -175,13 +174,6 @@ def options(self, parser, env): default=False, help="""Shortcut for --browser=comet""", ) - parser.addoption( - "--atlas", - action="store_true", - dest="use_atlas", - default=False, - help="""Shortcut for --browser=atlas""", - ) parser.addoption( "--use-chromium", "--chromium", @@ -1171,16 +1163,6 @@ def beforeTest(self, test): sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=comet") - if "--browser=atlas" in sys_argv or "--browser atlas" in sys_argv: - if not bin_loc_in_options: - bin_loc = detect_b_ver.get_binary_location("atlas") - if os.path.exists(bin_loc): - browser_changes += 1 - browser_set = "atlas" - sb_config._browser_shortcut = "atlas" - sb_config._cdp_browser = "atlas" - sb_config._cdp_bin_loc = bin_loc - browser_list.append("--browser=atlas") browser_text = browser_set if "--chrome" in sys_argv and not browser_set == "chrome": browser_changes += 1 @@ -1237,16 +1219,6 @@ def beforeTest(self, test): sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--comet") - if "--atlas" in sys_argv and not browser_set == "atlas": - if not bin_loc_in_options: - bin_loc = detect_b_ver.get_binary_location("atlas") - if os.path.exists(bin_loc): - browser_changes += 1 - browser_text = "atlas" - sb_config._browser_shortcut = "atlas" - sb_config._cdp_browser = "atlas" - sb_config._cdp_bin_loc = bin_loc - browser_list.append("--atlas") if browser_changes > 1: message = "\n\n TOO MANY browser types were entered!" message += "\n There were %s found:\n > %s" % ( @@ -1259,7 +1231,7 @@ def beforeTest(self, test): if browser_text: browser = browser_text if self.options.recorder_mode and browser not in [ - "chrome", "edge", "opera", "brave", "comet", "atlas", "chromium" + "chrome", "edge", "opera", "brave", "comet", "chromium" ]: message = ( "\n\n Recorder Mode ONLY supports Chromium browsers!" diff --git a/seleniumbase/undetected/cdp_driver/cdp_util.py b/seleniumbase/undetected/cdp_driver/cdp_util.py index 1f4639c8f04..8530df8250d 100644 --- a/seleniumbase/undetected/cdp_driver/cdp_util.py +++ b/seleniumbase/undetected/cdp_driver/cdp_util.py @@ -649,12 +649,10 @@ async def start( browser = "brave" elif "--comet" in sys_argv: browser = "comet" - elif "--atlas" in sys_argv: - browser = "atlas" else: browser = "chrome" sb_config._cdp_browser = browser - if browser == "comet" or browser == "atlas": + if browser == "comet": incognito = False guest = False with suppress(Exception): @@ -671,8 +669,6 @@ async def start( sb_config._cdp_browser = "brave" elif bin_loc.endswith("comet") or bin_loc.endswith("comet.exe"): sb_config._cdp_browser = "comet" - elif bin_loc.endswith("atlas") or bin_loc.endswith("atlas.exe"): - sb_config._cdp_browser = "atlas" else: sb_config._cdp_browser = "chrome" if ad_block: diff --git a/seleniumbase/undetected/cdp_driver/tab.py b/seleniumbase/undetected/cdp_driver/tab.py index 3099f38b02c..ef9399930c3 100644 --- a/seleniumbase/undetected/cdp_driver/tab.py +++ b/seleniumbase/undetected/cdp_driver/tab.py @@ -350,7 +350,7 @@ async def get( getattr(sb_config, "incognito", None) or ( getattr(sb_config, "_cdp_browser", None) - in ["comet", "atlas"] + in ["comet"] ) ): return await self.browser.get( diff --git a/setup.py b/setup.py index 7608a8f9368..7e7a1dfde1f 100755 --- a/setup.py +++ b/setup.py @@ -325,7 +325,6 @@ "seleniumbase.drivers.opera_drivers", "seleniumbase.drivers.brave_drivers", "seleniumbase.drivers.comet_drivers", - "seleniumbase.drivers.atlas_drivers", "seleniumbase.drivers.chromium_drivers", "seleniumbase.extensions", "seleniumbase.fixtures", From 9c0f401a9977efbb7d13114aaf98a52a0532af9c Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 17 Jul 2026 17:09:29 -0400 Subject: [PATCH 3/7] Remove Copilot examples (requires login now) --- .../cdp_mode/playwright/raw_copilot_async.py | 32 -------------- .../cdp_mode/playwright/raw_copilot_nested.py | 26 ----------- .../cdp_mode/playwright/raw_copilot_sync.py | 25 ----------- examples/cdp_mode/raw_cdp_copilot.py | 43 ------------------- examples/cdp_mode/raw_copilot.py | 43 ------------------- 5 files changed, 169 deletions(-) delete mode 100644 examples/cdp_mode/playwright/raw_copilot_async.py delete mode 100644 examples/cdp_mode/playwright/raw_copilot_nested.py delete mode 100644 examples/cdp_mode/playwright/raw_copilot_sync.py delete mode 100644 examples/cdp_mode/raw_cdp_copilot.py delete mode 100644 examples/cdp_mode/raw_copilot.py diff --git a/examples/cdp_mode/playwright/raw_copilot_async.py b/examples/cdp_mode/playwright/raw_copilot_async.py deleted file mode 100644 index 5f7fe57bc66..00000000000 --- a/examples/cdp_mode/playwright/raw_copilot_async.py +++ /dev/null @@ -1,32 +0,0 @@ -import asyncio -from playwright.async_api import async_playwright -from seleniumbase import cdp_driver - - -async def main(): - driver = await cdp_driver.start_async() - endpoint_url = driver.get_endpoint_url() - - async with async_playwright() as p: - browser = await p.chromium.connect_over_cdp(endpoint_url) - page = browser.contexts[0].pages[0] - await page.goto("https://copilot.microsoft.com") - await page.wait_for_selector("textarea#userInput") - await page.wait_for_timeout(1000) - query = "Playwright Python connect_over_cdp() sync example" - await page.fill("textarea#userInput", query) - await page.wait_for_timeout(2000) - await page.click('button[data-testid="submit-button"]') - await driver.sleep(5.25) - await driver.solve_captcha() - await page.wait_for_selector('button[data-testid*="-thumbs-up"]') - await page.wait_for_timeout(4000) - await page.click('button[data-testid*="scroll-to-bottom"]') - await page.wait_for_timeout(3000) - chat_results = '[data-testid="highlighted-chats"]' - result = await page.locator(chat_results).inner_text() - print(result.replace("\n\n", " \n")) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/examples/cdp_mode/playwright/raw_copilot_nested.py b/examples/cdp_mode/playwright/raw_copilot_nested.py deleted file mode 100644 index 9b3bb52f2e5..00000000000 --- a/examples/cdp_mode/playwright/raw_copilot_nested.py +++ /dev/null @@ -1,26 +0,0 @@ -from playwright.sync_api import sync_playwright -from seleniumbase import SB - -with SB(uc=True) as sb: - sb.activate_cdp_mode() - endpoint_url = sb.get_endpoint_url() - - with sync_playwright() as p: - browser = p.chromium.connect_over_cdp(endpoint_url) - page = browser.contexts[0].pages[0] - page.goto("https://copilot.microsoft.com") - page.wait_for_selector("textarea#userInput") - page.wait_for_timeout(2000) - query = "Playwright Python connect_over_cdp() sync example" - page.fill("textarea#userInput", query) - page.wait_for_timeout(2000) - page.click('button[data-testid="submit-button"]') - sb.sleep(5.25) - sb.solve_captcha() - page.wait_for_selector('button[data-testid*="-thumbs-up"]') - page.wait_for_timeout(4000) - page.click('button[data-testid*="scroll-to-bottom"]') - page.wait_for_timeout(3000) - chat_results = '[data-testid="highlighted-chats"]' - result = page.locator(chat_results).inner_text() - print(result.replace("\n\n", " \n")) diff --git a/examples/cdp_mode/playwright/raw_copilot_sync.py b/examples/cdp_mode/playwright/raw_copilot_sync.py deleted file mode 100644 index 71dfecfd600..00000000000 --- a/examples/cdp_mode/playwright/raw_copilot_sync.py +++ /dev/null @@ -1,25 +0,0 @@ -from playwright.sync_api import sync_playwright -from seleniumbase import sb_cdp - -sb = sb_cdp.Chrome() -endpoint_url = sb.get_endpoint_url() - -with sync_playwright() as p: - browser = p.chromium.connect_over_cdp(endpoint_url) - page = browser.contexts[0].pages[0] - page.goto("https://copilot.microsoft.com") - page.wait_for_selector("textarea#userInput") - page.wait_for_timeout(2000) - query = "Playwright Python connect_over_cdp() sync example" - page.fill("textarea#userInput", query) - page.wait_for_timeout(2000) - page.click('button[data-testid="submit-button"]') - sb.sleep(5.25) - sb.solve_captcha() - page.wait_for_selector('button[data-testid*="-thumbs-up"]') - page.wait_for_timeout(4000) - page.click('button[data-testid*="scroll-to-bottom"]') - page.wait_for_timeout(3000) - chat_results = '[data-testid="highlighted-chats"]' - result = page.locator(chat_results).inner_text() - print(result.replace("\n\n", " \n")) diff --git a/examples/cdp_mode/raw_cdp_copilot.py b/examples/cdp_mode/raw_cdp_copilot.py deleted file mode 100644 index 79732685a89..00000000000 --- a/examples/cdp_mode/raw_cdp_copilot.py +++ /dev/null @@ -1,43 +0,0 @@ -from contextlib import suppress -from seleniumbase import sb_cdp - -sb = sb_cdp.Chrome(locale="en", incognito=True) -sb.goto("https://copilot.microsoft.com/") -textarea = "textarea#userInput" -sb.wait_for_element(textarea) -sb.sleep(1.3) -sb.click_if_visible('[aria-label="Dismiss"]') -sb.sleep(1.2) -query = "How to start automating with SeleniumBase?" -sb.press_keys(textarea, query) -sb.sleep(1.1) -seen_text = sb.get_text(textarea) -if seen_text != query and seen_text in query: - # When CAPTCHA appears while typing text - sb.sleep(1.2) - sb.solve_captcha() - sb.sleep(2.2) - sb.type(textarea, "") - sb.press_keys(textarea, query) - sb.sleep(0.5) -sb.click('button[data-testid="submit-button"]') -sb.sleep(4.2) -sb.solve_captcha() -sb.sleep(2.8) -sb.solve_captcha() -sb.sleep(2.5) -stop_button = '[data-testid="stop-button"]' -thumbs_up = 'button[data-testid*="-thumbs-up-"]' -with suppress(Exception): - sb.wait_for_element_absent(stop_button, timeout=45) -with suppress(Exception): - sb.wait_for_element(thumbs_up, timeout=15) -sb.sleep(0.6) -scroll = 'button[data-testid*="scroll-to-bottom"]' -sb.click_if_visible(scroll) -sb.sleep(2.2) -folder = "downloaded_files" -file_name = "copilot_results.html" -sb.save_as_html(file_name, folder) -print('"./%s/%s" was saved!' % (folder, file_name)) -sb.quit() diff --git a/examples/cdp_mode/raw_copilot.py b/examples/cdp_mode/raw_copilot.py deleted file mode 100644 index 8a66f3d3739..00000000000 --- a/examples/cdp_mode/raw_copilot.py +++ /dev/null @@ -1,43 +0,0 @@ -from contextlib import suppress -from seleniumbase import SB - -with SB(uc=True, test=True, locale="en", incognito=True) as sb: - sb.activate_cdp_mode() - sb.goto("https://copilot.microsoft.com/") - textarea = "textarea#userInput" - sb.wait_for_element(textarea) - sb.sleep(1.3) - sb.click_if_visible('[aria-label="Dismiss"]') - sb.sleep(1.2) - query = "How to start automating with SeleniumBase?" - sb.press_keys(textarea, query) - sb.sleep(1.1) - seen_text = sb.get_text(textarea) - if seen_text != query and seen_text in query: - # When CAPTCHA appears while typing text - sb.sleep(1.2) - sb.solve_captcha() - sb.sleep(2.2) - sb.type(textarea, "") - sb.press_keys(textarea, query) - sb.sleep(0.5) - sb.click('button[data-testid="submit-button"]') - sb.sleep(4.2) - sb.solve_captcha() - sb.sleep(2.8) - sb.solve_captcha() - sb.sleep(2.5) - stop_button = '[data-testid="stop-button"]' - thumbs_up = 'button[data-testid*="-thumbs-up-"]' - with suppress(Exception): - sb.wait_for_element_absent(stop_button, timeout=45) - with suppress(Exception): - sb.wait_for_element(thumbs_up, timeout=15) - sb.sleep(0.6) - scroll = 'button[data-testid*="scroll-to-bottom"]' - sb.click_if_visible(scroll) - sb.sleep(2.2) - folder = "downloaded_files" - file_name = "copilot_results.html" - sb.save_page_source(file_name, folder) - print('"./%s/%s" was saved!' % (folder, file_name)) From aca0363058c95c4590ff6639e4e51d05010ddbe8 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 17 Jul 2026 17:16:21 -0400 Subject: [PATCH 4/7] Add a CDP Mode fingerprint test --- examples/cdp_mode/raw_cdp_clearcote.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 examples/cdp_mode/raw_cdp_clearcote.py diff --git a/examples/cdp_mode/raw_cdp_clearcote.py b/examples/cdp_mode/raw_cdp_clearcote.py new file mode 100644 index 00000000000..717e2007de4 --- /dev/null +++ b/examples/cdp_mode/raw_cdp_clearcote.py @@ -0,0 +1,20 @@ +from seleniumbase import sb_cdp + +sb = sb_cdp.Chrome() +sb.goto("https://www.clearcotelabs.com/audit") +sb.click('button:contains("Run the audit")') +sb.sleep(5) +sb.assert_element("div.text-emerald-700", timeout=6) +try: + sb.assert_text("100", "div.text-emerald-700", timeout=5) + sb.highlight('div.text-emerald-700:contains("100")') + print(" ✅ The browser fingerprint is clean! Score: 100") +except Exception: + score = sb.get_text("div.text-emerald-700") + print(f" ❌ Fingerprint tampering detected! Score: {score}") +sb.sleep(1) +folder = "downloaded_files" +file_name = "c_audit_results.pdf" +sb.save_as_pdf(file_name, folder) +print('"./%s/%s" was saved!' % (folder, file_name)) +sb.quit() From e152f11df548e76ec34822188a5234f6cb09cd57 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 17 Jul 2026 17:17:04 -0400 Subject: [PATCH 5/7] Update Stealthy Playwright Mode examples --- examples/cdp_mode/playwright/ReadMe.md | 87 +++++++++++++------ .../playwright/raw_footlocker_sync.py | 4 +- .../cdp_mode/playwright/raw_reddit_sync.py | 2 +- .../cdp_mode/playwright/raw_seatgeek_sync.py | 2 +- .../cdp_mode/playwright/raw_walmart_sync.py | 3 +- 5 files changed, 67 insertions(+), 31 deletions(-) diff --git a/examples/cdp_mode/playwright/ReadMe.md b/examples/cdp_mode/playwright/ReadMe.md index 3237348d9dd..33829d724e3 100644 --- a/examples/cdp_mode/playwright/ReadMe.md +++ b/examples/cdp_mode/playwright/ReadMe.md @@ -2,7 +2,7 @@

Stealthy Playwright Mode 🎭

-🎭 Stealthy Playwright Mode is a subset of **[SeleniumBase CDP Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md)** where [Playwright](https://github.com/microsoft/playwright-python) attaches to a stealthy browser session via the remote-debugging URL. This lets Playwright bypass bot-detection while allowing APIs of both frameworks to work in tandem. Under the hood, Playwright calls connect_over_cdp() to achieve this stealth. +🎭 Stealthy Playwright Mode is a subset of **[SeleniumBase CDP Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md)** where [playwright-python](https://github.com/microsoft/playwright-python) attaches to a stealthy browser session via the remote-debugging URL. This lets Playwright bypass bot-detection while allowing APIs of both frameworks to work in tandem. Under the hood, Playwright achieves stealth via connect_over_cdp(). -------- @@ -127,51 +127,88 @@ with sync_playwright() as p: ### 🎭 Stealthy Playwright Mode details: -The `sb_cdp` and `cdp_driver` formats don't use WebDriver at all, meaning that `chromedriver` isn't needed. From these two formats, Stealthy Playwright Mode can call [CDP Mode methods](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/cdp_mode_methods.md) and Playwright methods. +â„šī¸ The `sb_cdp` and `cdp_driver` formats don't use WebDriver at all, meaning that `chromedriver` isn't needed. From these two formats, Stealthy Playwright Mode can call [CDP Mode methods](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/cdp_mode_methods.md) and Playwright methods. -The `SB()` format requires WebDriver, therefore `chromedriver` will be downloaded, modified for stealth, and renamed as `uc_driver` if not already present. The `SB()` format has access to Selenium WebDriver methods via [the SeleniumBase API](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/method_summary.md). When using Stealthy Playwright Mode from the `SB()` format, all the APIs are accessible: Selenium, SeleniumBase, [UC Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md), [CDP Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md), and Playwright. +â„šī¸ The `SB()` format requires WebDriver, therefore `chromedriver` will be downloaded, modified for stealth, and renamed as `uc_driver` if not already present. The `SB()` format has access to Selenium WebDriver methods via [the SeleniumBase API](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/method_summary.md). When using Stealthy Playwright Mode from the `SB()` format, all the APIs are accessible: Selenium, SeleniumBase, [UC Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md), [CDP Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md), and Playwright. -Default timeout values are different between Playwright and SeleniumBase. For instance, a 30-second default timeout in a Playwright method might only be 10 seconds in the equivalent SeleniumBase method. +â„šī¸ Default timeout values are different between Playwright and SeleniumBase. For instance, a 30-second default timeout in a Playwright method might only be 10 seconds in the equivalent SeleniumBase method. -When specifying custom timeout values, Playwright uses milliseconds, whereas SeleniumBase uses seconds. Eg. `page.wait_for_timeout(2000)` in Playwright is the equivalent of `sb.sleep(2)` in SeleniumBase. +â„šī¸ When specifying custom timeout values, Playwright uses milliseconds, whereas SeleniumBase uses seconds. Eg. `page.wait_for_timeout(2000)` in Playwright is the equivalent of `sb.sleep(2)` in SeleniumBase. -Although hard sleeps are generally discouraged, they become a tactical tool in stealth mode because that extra waiting helps the automation look more human. Hard sleeps are used in multiple examples to prevent rate limits from being exceeded. +â„šī¸ Although hard sleeps are generally discouraged, they become a tactical tool in stealth mode because that extra waiting helps the automation look more human. Hard sleeps are used in multiple examples to prevent rate limits from being exceeded. + +🌠 For additional stealth, use `sb.goto(URL)` instead of `page.goto(URL)`. -------- ### 🎭 A few examples of Stealthy Playwright Mode: -🎭 Here's an example that queries Microsoft Copilot: +🎭 Here's an example that searches SeatGeek: ```python from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp -sb = sb_cdp.Chrome() +sb = sb_cdp.Chrome(locale="en", ad_block=True) +sb.goto("https://seatgeek.com/") endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) page = browser.contexts[0].pages[0] - page.goto("https://copilot.microsoft.com") - page.wait_for_selector("textarea#userInput") - page.wait_for_timeout(2000) - query = "Playwright Python connect_over_cdp() sync example" - page.fill("textarea#userInput", query) - page.wait_for_timeout(2000) - page.click('button[data-testid="submit-button"]') - sb.sleep(5.25) - sb.solve_captcha() - page.wait_for_selector('button[data-testid*="-thumbs-up"]') - page.wait_for_timeout(4000) - page.click('button[data-testid*="scroll-to-bottom"]') - page.wait_for_timeout(3000) - chat_results = '[data-testid="highlighted-chats"]' - result = page.locator(chat_results).inner_text() - print(result.replace("\n\n", " \n")) + input_field = 'input[name="search"]' + page.wait_for_selector(input_field) + page.wait_for_timeout(1600) + query = "Jerry Seinfeld" + search_box = page.locator(input_field) + search_box.press_sequentially(query, delay=80) + page.wait_for_timeout(1600) + page.click("li#active-result-item") + page.wait_for_timeout(4200) + print('*** SeatGeek Search for "%s":' % query) + items = page.locator('[data-testid="listing-item"]') + for i in range(items.count()): + item_text = items.nth(i).inner_text() + print(item_text.replace("\n\n", "\n")) +``` + +(From [examples/cdp_mode/playwright/raw_seatgeek_sync.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/playwright/raw_seatgeek_sync.py)) + +🎭 Here's an example that searches Foot Locker: + +```python +from playwright.sync_api import sync_playwright +from seleniumbase import sb_cdp + +sb = sb_cdp.Chrome(locale="en", ad_block=True) +sb.goto("https://www.footlocker.com/") +endpoint_url = sb.get_endpoint_url() + +with sync_playwright() as p: + browser = p.chromium.connect_over_cdp(endpoint_url) + page = browser.contexts[0].pages[0] + input_field = 'input[name="query"]' + page.wait_for_selector(input_field) + sb.sleep(1.5) + sb.click_if_visible('button[id*="Agree"]') + sb.sleep(1.2) + page.click(input_field) + sb.sleep(0.5) + search = "Nike Shoes" + page.type(input_field, search) + sb.sleep(1.2) + page.click('ul[id*="typeahead"] li div') + sb.sleep(3.5) + elements = sb.select_all("a.ProductCard-link") + if elements: + print('**** Found results for "%s": ****' % search) + for element in elements: + print("------------------ >>>") + print("* " + element.text) + sb.sleep(2) ``` -(From [examples/cdp_mode/playwright/raw_copilot_sync.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/playwright/raw_copilot_sync.py)) +(From [examples/cdp_mode/playwright/raw_footlocker_sync.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/playwright/raw_footlocker_sync.py)) 🎭 Here's an example that solves the Bing CAPTCHA: diff --git a/examples/cdp_mode/playwright/raw_footlocker_sync.py b/examples/cdp_mode/playwright/raw_footlocker_sync.py index d6d43fc2591..af4b9a2e4e9 100644 --- a/examples/cdp_mode/playwright/raw_footlocker_sync.py +++ b/examples/cdp_mode/playwright/raw_footlocker_sync.py @@ -2,12 +2,12 @@ from seleniumbase import sb_cdp sb = sb_cdp.Chrome(locale="en", ad_block=True) +sb.goto("https://www.footlocker.com/") endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) page = browser.contexts[0].pages[0] - page.goto("https://www.footlocker.com/") input_field = 'input[name="query"]' page.wait_for_selector(input_field) sb.sleep(1.5) @@ -16,7 +16,7 @@ page.click(input_field) sb.sleep(0.5) search = "Nike Shoes" - sb.press_keys(input_field, search) + page.type(input_field, search) sb.sleep(1.2) page.click('ul[id*="typeahead"] li div') sb.sleep(3.5) diff --git a/examples/cdp_mode/playwright/raw_reddit_sync.py b/examples/cdp_mode/playwright/raw_reddit_sync.py index d880dd4c665..4d614e1025e 100644 --- a/examples/cdp_mode/playwright/raw_reddit_sync.py +++ b/examples/cdp_mode/playwright/raw_reddit_sync.py @@ -9,7 +9,7 @@ page = browser.contexts[0].pages[0] search = "reddit+scraper" url = f"https://www.reddit.com/r/webscraping/search/?q={search}" - page.goto(url) + sb.goto(url) sb.solve_captcha() # Might not be needed page.wait_for_timeout(1000) post_title = '[data-testid="post-title"]' diff --git a/examples/cdp_mode/playwright/raw_seatgeek_sync.py b/examples/cdp_mode/playwright/raw_seatgeek_sync.py index a9f1b3b7530..3de2cf96af8 100644 --- a/examples/cdp_mode/playwright/raw_seatgeek_sync.py +++ b/examples/cdp_mode/playwright/raw_seatgeek_sync.py @@ -2,12 +2,12 @@ from seleniumbase import sb_cdp sb = sb_cdp.Chrome(locale="en", ad_block=True) +sb.goto("https://seatgeek.com/") endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) page = browser.contexts[0].pages[0] - page.goto("https://seatgeek.com/") input_field = 'input[name="search"]' page.wait_for_selector(input_field) page.wait_for_timeout(1600) diff --git a/examples/cdp_mode/playwright/raw_walmart_sync.py b/examples/cdp_mode/playwright/raw_walmart_sync.py index 2fec140a01d..7fb2840ed28 100644 --- a/examples/cdp_mode/playwright/raw_walmart_sync.py +++ b/examples/cdp_mode/playwright/raw_walmart_sync.py @@ -27,9 +27,8 @@ page.wait_for_timeout(1200) page.wait_for_selector('[data-item-id]', timeout=10000) page.wait_for_timeout(600) - for i in range(17): + for i in range(10): sb.scroll_down(16) - sb.sleep(0.05) items = page.locator('[data-item-id]') for i in range(items.count()): item = items.nth(i) From 93de30adf71d6b3277512ab78f1ddf1eb0b586d3 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 17 Jul 2026 17:17:51 -0400 Subject: [PATCH 6/7] Refresh Python dependencies --- requirements.txt | 4 ++-- setup.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index c7f789d8786..a45cab450a1 100755 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ exceptiongroup>=1.3.1 websockets~=15.0.1;python_version<"3.10" websockets>=16.1;python_version>="3.10" filelock~=3.19.1;python_version<"3.10" -filelock>=3.29.7;python_version>="3.10" +filelock>=3.30.3;python_version>="3.10" fasteners>=0.20 mycdp>=1.4.0 pynose>=1.5.5 @@ -80,7 +80,7 @@ rich>=15.0.0,<16 # ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.) coverage>=7.10.7;python_version<"3.10" -coverage>=7.15.1;python_version>="3.10" +coverage>=7.15.2;python_version>="3.10" pytest-cov>=7.1.0 flake8==7.3.0 mccabe==0.7.0 diff --git a/setup.py b/setup.py index 7e7a1dfde1f..e23737f782f 100755 --- a/setup.py +++ b/setup.py @@ -174,7 +174,7 @@ 'websockets~=15.0.1;python_version<"3.10"', 'websockets>=16.1;python_version>="3.10"', 'filelock~=3.19.1;python_version<"3.10"', - 'filelock>=3.29.7;python_version>="3.10"', + 'filelock>=3.30.3;python_version>="3.10"', 'fasteners>=0.20', 'mycdp>=1.4.0', 'pynose>=1.5.5', @@ -253,7 +253,7 @@ # Usage: coverage run -m pytest; coverage html; coverage report "coverage": [ 'coverage>=7.10.7;python_version<"3.10"', - 'coverage>=7.15.1;python_version>="3.10"', + 'coverage>=7.15.2;python_version>="3.10"', 'pytest-cov>=7.1.0', ], # pip install -e .[flake8] From e4f803f5adc47880c5563c7ae528d1a190249865 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 17 Jul 2026 17:18:40 -0400 Subject: [PATCH 7/7] Version 4.51.2 --- seleniumbase/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index 28aece1764b..797f41bb2d0 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.51.1" +__version__ = "4.51.2"