playwright-webdriver-py
Run existing Selenium WebDriver Python scripts using Playwright as the browser backend — no ChromeDriver or browser binary installation required.
playwright-webdriver-py is a drop-in compatibility layer that implements the Selenium WebDriver Python API over Playwright's synchronous API. Drop it in and your existing webdriver.Chrome() scripts run backed by Playwright; faster, more reliable, and without the ChromeDriver version-matching headache.
Features
- Zero script changes — existing Selenium scripts run as-is
- No ChromeDriver — Playwright manages its own browser binaries
- Full
WebDriverWait/expected_conditionssupport — unchanged from Selenium - Chromium, Firefox, and WebKit backends via
PlaywrightBrowserType - CLI runner — execute any
.pySelenium script directly from the command line PlaywrightBy— opt-in raw Playwright selector strings for power users
Installation
pip install playwright-webdriver-py
playwright install # downloads browser binaries (first time only)
Or with Poetry:
poetry add playwright-webdriver-py
playwright install
Requirements: Python 3.12+
Quick Start
Run an existing Selenium script unchanged
# my_existing_script.py — written for standard Selenium, runs on Playwright
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
wait = WebDriverWait(driver, 10)
driver.get("https://www.wikipedia.org")
search = wait.until(EC.presence_of_element_located((By.ID, "searchInput")))
search.send_keys("Playwright (software)")
search.submit()
heading = wait.until(EC.presence_of_element_located((By.ID, "firstHeading")))
print(heading.text)
driver.quit()
Via the CLI runner (intercepts webdriver.Chrome() transparently):
python -m playwright_webdriver.runner my_existing_script.py
python -m playwright_webdriver.runner my_existing_script.py --headless
Via the Python API (same interception, callable from your own code):
from playwright_webdriver.runner import run_selenium_script
run_selenium_script("my_existing_script.py", headless=True)
Use PlaywrightWebDriver directly
from playwright_webdriver import PlaywrightWebDriver, PlaywrightBrowserType
from selenium.webdriver.common.by import By
with PlaywrightWebDriver.create(
browser_type=PlaywrightBrowserType.CHROMIUM,
launch_options={"headless": True},
) as driver:
driver.get("https://example.com")
heading = driver.find_element(By.TAG_NAME, "h1")
print(heading.text) # "Example Domain"
Supported Selenium API
PlaywrightWebDriver
| Method / Property | Notes |
|---|---|
driver.get(url) |
Navigates to URL |
driver.find_element(by, value) |
Returns PlaywrightWebElement |
driver.find_elements(by, value) |
Returns list[PlaywrightWebElement] |
driver.execute_script(script, *args) |
Full JS execution; arguments[N] supported |
driver.current_url |
Current page URL |
driver.title |
Page title |
driver.page_source |
Full HTML source |
driver.maximize_window() |
Sets viewport to 1920×1080 |
driver.close() |
Closes current tab |
driver.quit() |
Closes browser and releases all resources |
driver.navigate() |
Returns navigation object (back, forward, refresh, to) |
driver.switch_to() |
Returns target locator (frame, window, alert, default_content) |
driver.manage() |
Returns options object (cookies, timeouts, window) |
driver.current_window_handle |
Handle for the current window/tab |
driver.window_handles |
All open window/tab handles |
PlaywrightWebElement
| Method / Property | Notes |
|---|---|
element.click() |
Clicks the element |
element.send_keys(*value) |
Text input; Selenium Keys constants supported |
element.clear() |
Clears a text field |
element.submit() |
Submits the form containing the element |
element.find_element(by, value) |
Scoped element search |
element.find_elements(by, value) |
Scoped element list |
element.get_attribute(name) |
HTML attribute value |
element.get_property(name) |
DOM property value |
element.get_dom_attribute(name) |
DOM attribute (no fallback to property) |
element.text |
Visible text content |
element.tag_name |
Element tag name |
element.is_displayed() |
True if the element is visible |
element.is_enabled() |
True if the element is enabled |
element.is_selected() |
True if a checkbox/radio is checked |
Locator strategies
All standard Selenium By strategies are supported:
By constant |
Playwright selector |
|---|---|
By.ID |
#value |
By.NAME |
[name='value'] |
By.CLASS_NAME |
.value |
By.CSS_SELECTOR |
css=value |
By.XPATH |
xpath=value |
By.TAG_NAME |
bare tag selector |
By.LINK_TEXT |
a:visible:text-is("value") (visible only, matching Selenium semantics) |
By.PARTIAL_LINK_TEXT |
a:visible:has-text("value") (visible only, matching Selenium semantics) |
In addition, PlaywrightBy.selector(string) passes a raw Playwright selector string through unchanged, giving you access to Playwright-specific features like role=, text=, and :visible filters:
from playwright_webdriver import PlaywrightBy
btn = driver.find_element(PlaywrightBy.selector("role=button[name='Submit']"))
Running Tests
The test suite is a Python port of the original C# NUnit test suite from Selenium.PlaywrightDriver.
# Install dev dependencies
poetry install
# Run the full suite (19 tests, all headless)
pytest tests/
All tests use local HTML fixtures and an ephemeral HTTP server — no internet connection required.
Examples
The examples/ directory contains real-world Selenium scripts that run unmodified through the CLI runner:
python -m playwright_webdriver.runner examples/wikipedia_smoke_test.py
python -m playwright_webdriver.runner examples/apple_homepod_test.py
python -m playwright_webdriver.runner examples/skyscanner_one_way_search_test.py
How It Works
The CLI runner monkey-patches selenium.webdriver.Chrome (and Firefox, Edge, Remote, etc.) on the real selenium.webdriver module before exec()-ing the script. This means:
WebDriverWait,expected_conditions,Keys, and all other Selenium helpers continue to import from the real Selenium package — nothing breaks.- The only substitution is the browser factory:
webdriver.Chrome()returns aPlaywrightWebDriverinstance backed by Playwright's Chromium. - All patches are restored in a
finallyblock, so running multiple scripts in the same process is safe.
License
MIT © 2026 HappyPath. See LICENSE for details.
Metadata
Release files for playwright-webdriver-py 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| playwright_webdriver_py-0.1.0.tar.gz | 20.0 kB | With provenance | Details for playwright_webdriver_py-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| playwright_webdriver_py-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for playwright_webdriver_py-0.1.0-py3-none-any.whl |
Total release size: 41.3 kB
Release files / playwright_webdriver_py-0.1.0.tar.gz
| Download URL | playwright_webdriver_py-0.1.0.tar.gz |
|---|---|
| Size | 20.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3c063642677e327aa9f2949da4bcc6c92fc83779837b4dc197663a88e0a396cd
|
|
BLAKE2b-256 checksum How to use checksums |
a4c571a1216a0b2383f9c0ccbb7168d6765c8b88a491396f7b3c02468dbec52c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 4, 2026.
Transparency logRelease files / playwright_webdriver_py-0.1.0-py3-none-any.whl
| Download URL | playwright_webdriver_py-0.1.0-py3-none-any.whl |
|---|---|
| Size | 21.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
eedbf26ab41c8a10df08b0b2f90f2ca39764338ae8851cd845dda2aaed3f5c09
|
|
BLAKE2b-256 checksum How to use checksums |
e76c0c21f80642ce4d09c9bd60837ea4d1177612d408a7941c84c4c4e85361f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 4, 2026.
Transparency logSecurity for playwright-webdriver-py 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for playwright_webdriver_py-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 4, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.