Open-source project
seleniumbase/SeleniumBase avatar
seleniumbase/SeleniumBase

SeleniumBase CDP Mode, exact dependency pins, and the stealth it does not cover

Browser automation, web scraping, and testing. CDP Mode provides a Stealthy Chromium instance that bypasses bot-detection systems. Includes an MCP server.

13,252 stars1,616 forksPythonMIT

At a glance

What is it?
SeleniumBase wraps Selenium 4.x for pytest-driven end to end testing and adds a CDP Mode that drives Chromium without WebDriver. The tree is wide, key packages such as selenium and pytest are pinned exactly, and the stealth layer is narrower than the headline suggests.
Who is it for?
Judge SeleniumBase on the testing side if you already run pytest and want reports, screenshots, and a recorder wrapped around WebDriver. Judge CDP Mode on one thing: whether the detectors you actually hit treat your address and account as automated.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • seleniumbase/SeleniumBase13,252 ★ · +221
Star history
Repository2026-09-222026-11-13
seleniumbase/SeleniumBase13k13.3k

221 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

CDP Mode reaches Chromium without a chromedriver process in the path

SeleniumBase ships two ways of reaching a browser, and CDP Mode is the one that skips WebDriver entirely. The object returned by sb_cdp.Chrome() carries its own method names instead of the driver, find_element, by chain you write against raw Selenium.

python
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
sb.goto("https://browserscan.net/bot-detection")
sb.sleep(3)
sb.quit()

No chromedriver process sits in that path, and nothing in the example is a Selenium-typed element handle. Navigation is a method on the browser object rather than an attribute you assign, so an object built this way cannot be handed to code expecting a standard WebDriver session. The tree isolates that driver in its own package, seleniumbase.undetected.cdp_driver, sitting beside seleniumbase.undetected, which is how the project separates the non-WebDriver path from the rest of the layout. Migration material therefore lives in its own directory, examples/migration/raw_selenium, because code written against raw Selenium has to be reshaped rather than subclassed.

Stealthy Playwright Mode exports the same session instead of replacing it

Stealthy Playwright Mode does not reimplement the stealth. It exports the session you already have. You start sb_cdp with guest mode turned on, take the endpoint URL off the browser object, and let Playwright attach over the DevTools protocol.

python
from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome(guest=True)
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://bot.sannysoft.com/")
    page.wait_for_timeout(500)

The page handle you get back is a Playwright page, not a SeleniumBase one, so from that line onward you are inside Playwright's API and the CDP method names are gone. The guest=True flag exists to make that handoff work, and the Playwright package is a separate install from the framework itself:

bash
pip install seleniumbase
pip install playwright

The consequence is that a scraper built this way has two dependency trees to pin.

Raw CDP methods put interaction calls on the browser object with no wait arguments

The example file examples/cdp_mode/raw_cdp_methods.py chains interaction methods directly on the browser object. type writes into an element, press_keys types one character at a time, click takes a CSS selector, set_value drives a range input to a given number, click_visible_elements hits every match of a class, and select_option_by_text picks by option label.

python
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
sb.goto("https://seleniumbase.io/demo_page")
sb.type("input", "Quickly type text!")
sb.press_keys("textarea", "Slowly type text!")
sb.click("#myButton")
sb.set_value("input#mySlider", "100")
sb.click_visible_elements("input.checkBoxClassB")

None of those calls take an explicit wait, a retry count, or a timeout. The demo page used here, seleniumbase.io/demo_page, is published by the project for exactly this, which also means it is not a neutral target to measure your selectors against.

requirements.txt resolves to different code depending on your interpreter version

requirements.txt mixes ranges with exact pins; the exact ones include selenium==4.49.0, behave==1.2.6, parameterized==0.9.0, execnet==2.1.2, sortedcontainers==2.4.0 and pluggy==1.6.0. Three packages are selected by interpreter version, so the same file resolves to different code on 3.10 and 3.11.

text
pytest==8.4.2;python_version<"3.11"
pytest==9.1.1;python_version>="3.11"
websockets~=16.1.1;python_version=="3.10"
websockets>=16.1.1;python_version>="3.11"

pytest-rerunfailures splits the same way, at 16.3 for 3.10 and 16.7 for 3.11 and later. Platform markers appear too: python-xlib==0.33 and PyAutoGUI>=0.9.54 install only on Linux, and pyreadline3>=3.5.4 only on Windows. setup.py states the project uses selenium 4.x and is compatible with Python 3.10+, and it refuses to publish to PyPI below 3.10.

The practical consequence is that the SeleniumBase you get depends on the interpreter you happen to be running, not only on the version you asked pip for. A suite that passes on 3.10 can fail on 3.11 for reasons that have nothing to do with your test code, and the difference is a different pytest major version. Record the interpreter in your CI image for that reason. The same file also pins websockets, trio, wsproto and websocket-client together, so a conflict from any other package in your environment will surface here.

CDP Mode hides the driver process, and nothing in the API changes your address

The README says CDP Mode bypasses bot-detection with Chromium-based browsers. The only argument shown for sb_cdp.Chrome() anywhere is guest=True in the Playwright handoff. Neither example configures a proxy, sets a user agent, or seeds a cookie jar. What the README claims is narrower than the headline: CDP Mode bypasses bot detection with Chromium-based browsers, with one example annotated that all BrowserScan bot-detection tests passed and the other annotated that all Sannysoft bot-detection tests passed. Those are the project's own claims about two named fingerprint pages, not a statement about any site you have not run against.

The consequence for a scraper is specific. Dropping chromedriver from the process list removes one signal. The address your requests come from is still whatever the machine you run on has, and no argument in the documented API changes that. When a target blocks you, the stealth layer reports nothing about whether it was the deciding factor, so you can lose hours tuning waits and selectors that were never at fault. Verify against the detector you actually care about instead of counting the two demo pages as coverage, and treat a block as a signal about your hosting setup before you treat it as a bug in your script.

An editable install leaves out the coverage and lint toolchain entirely

The testing requirements sit in a commented block at the bottom of requirements.txt, separate from the runtime list: coverage>=7.16.1, pytest-cov>=7.1.0, flake8==7.4.1, mccabe==0.7.0, pyflakes==4.0.1, pycodestyle==2.15.0. A comment in that file states that pip install -r requirements.txt installs this group, and that pip install -e. will not.

So a contributor who clones the tree and runs an editable install gets the framework without coverage, without the pytest coverage plugin, and without the exact flake8 that a release is checked against. Running the suite then fails on a missing plugin, and the error names pytest rather than SeleniumBase, which sends you looking in the wrong tree. setup.py installs flake8==7.4.1 only when sys.argv[-1] equals publish, so the linter that guards a release is not present in an ordinary working checkout either.

The cost is a confusing first hour in a new clone. You need the full requirements file rather than the package install, and you need to know that the difference between a broken test and a missing tool lives in the last block of that file. Contributing work is where this bites hardest, since the CI configuration is a separate azure-pipelines.yml rather than something the checkout sets up for you.

The Docker image fixes one timezone, one locale, and one font set for every run

The container image is pinned tightly enough that what your tests observe can depend on it. The base is ubuntu:24.04, and the build sets TZ=America/New_York, links /etc/localtime from that zone, sets LANG and LC_ALL to en_US.UTF-8, and writes those values into /etc/environment and /etc/locale.conf before running locale-gen. Fonts are installed by name, including fonts-noto-color-emoji, fonts-liberation2, fonts-open-sans, fonts-roboto, fonts-droid-fallback and fonts-freefont-ttf, alongside the libraries a headless Chromium needs: libgbm1, libnss3, libatk-bridge2.0-0, libcups2, libxcomposite1, libxrandr2, libpango-1.0-0, libcairo2 and libvulkan1.

The consequence is that anything outside the DOM can differ between your laptop and the image. A page that measures text with a font your machine lacks renders with a different fallback inside the container, and a date assertion that reads the system clock sees New York rather than your own zone. A visual comparison that looked stable locally can shift when Liberation or the emoji font resolves as the image expects. Pin the image by digest, keep clock and locale assertions out of screenshot baselines, and treat a visual diff failure in CI as a possible environment difference before you treat it as a real UI change.

Recent releases move the dependency graph rather than the API surface

The latest releases are 4.54.10 (MCP Server: Patch 18) on 2026-09-18, 4.54.9 (MCP Server: Patch 17) on 2026-09-17 and 4.54.8 (Fix command injection and more) on 2026-09-17. The last push to the repository is dated 2026-09-18 and the project is not archived. Read together, three releases in two days are mostly MCP server patches, dependency refreshes and a command-injection fix in the console scripts rather than new interfaces.

The root shows how much surface those pins cover. install.sh, virtualenv_install.sh, win_install.bat and win_virtualenv.bat sit next to setup.py, setup.cfg, pyproject.toml and pytest.ini, alongside mcp_servers/, integrations/, sbase/, seleniumbase/, help_docs/ and examples/. The package list in pyproject.toml spans seleniumbase, sbase and mcp_servers, plus subpackages for behave, fixtures, plugins, translate, undetected and undetected.cdp_driver, with a drivers tree holding separate modules for cft, chs, opera, brave, comet and chromium. The project description also states it includes an MCP server.

What that means for adoption is a maintenance bill. Because requirements.txt pins several packages exactly, an older checkout pinned to selenium==4.49.0 is already behind a graph that moved three times in two days. Read each release note before upgrading, since all three releases refresh Python dependencies.

Editorial conclusion

Judge SeleniumBase on the testing side if you already run pytest and want reports, screenshots, and a recorder wrapped around WebDriver. Judge CDP Mode on one thing: whether the detectors you actually hit treat your address and account as automated. Before committing, confirm that your pinned selenium==4.49.0 still matches the release you read about, and install from requirements.txt rather than an editable install so the coverage and lint tools exist.

Frequently asked questions

how to install seleniumbase

The documented path is pip install seleniumbase, with pip install playwright as a separate step for the optional Playwright integration. setup.py states the framework uses selenium 4.x and is compatible with Python 3.10+, and the repository also ships install.sh, virtualenv_install.sh, win_install.bat and win_virtualenv.bat alongside requirements.txt.

what is seleniumbase

It is a Python framework described in setup.py as being for web crawling, scraping, and testing, with support for pytest and many tools. The project description adds that CDP Mode provides a Stealthy Chromium instance that bypasses bot detection, and that an MCP server is included.

seleniumbase vs selenium

SeleniumBase builds on Selenium: requirements.txt pins selenium==4.49.0 and setup.py states it uses selenium 4.x. The difference it adds is a wrapper layer of pytest fixtures and its own CDP Mode, which drives Chromium through seleniumbase.undetected.cdp_driver instead of going through WebDriver.

seleniumbase vs undetected chromedriver

SeleniumBase carries its own undetected packages in the wheel, seleniumbase.undetected and seleniumbase.undetected.cdp_driver, both listed in pyproject.toml. CDP Mode reaches that path through sb_cdp.Chrome(), which returns a browser object with its own method names such as goto and click instead of a WebDriver session.

is seleniumbase safe

The project is MIT licensed and not archived, with its last push dated 2026-09-18. Note that CDP Mode exists specifically to bypass bot detection, so any site you point it at sees an automated browser regardless of what the license grants you.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. seleniumbase/SeleniumBase on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/seleniumbase-seleniumbase.svg)](https://hysenlabs.com/projects/seleniumbase-seleniumbase)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/seleniumbase-seleniumbase.svg)](https://hysenlabs.com/projects/seleniumbase-seleniumbase)

Was this useful?