Skip to content

Migrating from undetected-chromedriver

Clearcote is driven over CDP, not WebDriver, so the move is from Selenium to Playwright. With no ChromeDriver there is no driver to patch or keep in step with the browser. The same steps work for selenium-stealth.

The change

import undetected_chromedriver as uc
from selenium.webdriver.common.by import By
from clearcote import launch

options = uc.ChromeOptions()
options.add_argument("--proxy-server=http://proxy.example:8080")
driver = uc.Chrome(options=options, headless=True)
browser = launch(
    fingerprint="acct-1",                    # one seed per account: the same device every run
    proxy={"server": "http://proxy.example:8080"},
    geoip=True,                              # timezone, language and WebRTC follow the exit IP
    humanize=True,                           # human mouse paths, typing and scrolling
)
page = browser.new_page()
driver.get("https://example.com")
driver.find_element(By.CSS_SELECTOR, "a").click()
print(driver.title)
driver.quit()
page.goto("https://example.com")
page.locator("a").first.click()
print(page.title())
browser.close()

launch() downloads a verified Clearcote build on first use, starts it and returns a Playwright Browser.

Selenium to Playwright, call by call

SeleniumPlaywright
driver.get(url)page.goto(url)
driver.find_element(By.CSS_SELECTOR, s)page.locator(s)
driver.find_element(By.XPATH, x)page.locator("xpath=" + x)
el.click(), el.send_keys("x")loc.click(), loc.fill("x") or loc.press_sequentially("x")
el.text, el.get_attribute("href")loc.inner_text(), loc.get_attribute("href")
WebDriverWait(driver, 10).until(...)Usually nothing: actions wait for the element. Otherwise loc.wait_for()
driver.execute_script(js)page.evaluate(js)
driver.get_cookies(), add_cookiepage.context.cookies(), add_cookies
driver.save_screenshot(path)page.screenshot(path=path)
driver.switch_to.frame(f)page.frame_locator(s)
driver.switch_to.window(h)Each tab is its own page; context.pages lists them

Options

BeforeWith Clearcote
stealth(driver, vendor=..., webgl_vendor=..., renderer=..., platform=...)Remove it. The persona supplies these values, and they agree with the GPU, the fonts and the user agent
a proxy extension for a loginproxy={"server", "username", "password"} on launch(); needs the latest build (below)
--user-agent, --lang, --window-sizeRemove them, or use accept_language and timezone; geoip=True sets both from the proxy
user_data_dir=launch_persistent_context(user_data_dir, ...)
version_main=, driver patchingNothing to patch: there is no ChromeDriver

A large Selenium suite?

Selenium and WebDriver are not supported. ChromeDriver can attach to a browser started with serve() through debugger_address, but the driver then brings back the WebDriver surface this move is meant to remove (see How detection works). Use it only while you port, and move the important flows first.

python
from urllib.parse import urlparse
from clearcote import serve
from selenium import webdriver

with serve(fingerprint="acct-1") as srv:          # unsupported: for the transition only
    options = webdriver.ChromeOptions()
    options.debugger_address = urlparse(srv.cdp_url).netloc
    driver = webdriver.Chrome(options=options)
    driver.get("https://example.com")
    driver.quit()

Check the switch

clearcote info prints the build the SDK resolves and a launch test. Without a licence key that is the open build (BSD-3, no account); a proxy with a username and password needs the latest build, free with a GitHub sign-in (clearcote login). Then open the audit in the new setup and compare. Comparisons: undetected-chromedriver and selenium-stealth.