Looking to super‑charge your web testing or data‑scraping projects with a modern, reliable tool? Python Playwright offers a fast, cross‑browser automation library that works with Chromium, Firefox, and WebKit—all from a clean, Pythonic API. In this guide we’ll walk through everything you need to know to get started, write robust scripts, and integrate Playwright into larger test suites. Whether you’re a beginner or an experienced QA engineer, you’ll find practical examples, best‑practice tips, and SEO‑friendly keywords to help your content rank higher in search results.
What Is Playwright and Why Choose It for Python?
Playwright was created by the same team behind Microsoft Edge and quickly became a favorite for developers who need:
- Cross‑browser support: One script runs on Chromium, Firefox, and WebKit.
- Auto‑waiting: Built‑in smart waits eliminate flaky tests.
- Rich selector engine: CSS, text, XPath, and custom selectors.
- Headless and headed modes: Perfect for CI pipelines or interactive debugging.
Compared to Selenium, Playwright offers faster execution, better handling of modern web features (like Shadow DOM), and a more intuitive async API—making it a top choice for Python developers.
Installing Playwright for Python
Step‑by‑step installation
pip install playwright
# Install the browsers (Chromium, Firefox, WebKit)
python -m playwright install
For projects that use asyncio, you’ll also want the async version of the library:
pip install playwright[async]
If you prefer a virtual environment (highly recommended), set it up before installing:
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install --upgrade pip
Writing Your First Playwright Script
Synchronous example
from playwright.sync_api import sync_playwright
def run():
with sync_playwright() as p:
# Launch Chromium in headless mode
browser = p.chromium.launch(headless=True)
page = browser.new_page()
# Navigate to a website
page.goto("https://example.com")
# Capture a screenshot
page.screenshot(path="example.png")
# Extract the page title
print("Page title:", page.title())
browser.close()
if __name__ == "__main__":
run()
Asynchronous version
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.firefox.launch(headless=False)
page = await browser.new_page()
await page.goto("https://news.ycombinator.com")
await page.screenshot(path="hn.png")
print("Title:", await page.title())
await browser.close()
asyncio.run(main())
Both snippets demonstrate the core workflow: launch → new_page → goto → actions → close. The auto‑waiting feature means you rarely need explicit time.sleep() calls.
Understanding Selectors in Playwright
Playwright’s selector engine is flexible. Here are the most common patterns:
page.click("button#submit")– CSS selector.page.fill("input[name='q']", "Playwright")– Fill a text field.page.click("text=Login")– Text selector (matches visible text).page.locator("xpath=//div[@class='price']")– XPath.page.locator("data-test-id=nav-item")– Custom attribute selector.
When dealing with Shadow DOM or iframes, use the shadow and frame helpers:
# Click a button inside a shadow root
page.locator("css=custom-element").shadow_root().click("button")
# Switch to an iframe
frame = page.frame(name="payment-frame")
await frame.fill("input#card-number", "4242424242424242")
Smart Waiting and Assertions
One of Playwright’s biggest advantages is its built‑in waiting mechanism. Instead of manually adding delays, you can rely on:
page.wait_for_selector()– Waits until an element appears.page.wait_for_load_state()– Waits forload,domcontentloaded, ornetworkidle.expect(page).to_have_url()– Assertion that automatically waits.
from playwright.sync_api import expect
page.goto("https://example.com/login")
page.fill("input#email", "user@example.com")
page.click("button#next")
# Wait until the password field is visible
page.wait_for_selector("input#password")
page.fill("input#password", "Secret123")
page.click("button#login")
# Verify successful navigation
expect(page).to_have_url("**/dashboard")
These helpers dramatically reduce flaky test failures caused by timing issues.
Capturing Screenshots, PDFs, and Video
Visual artifacts are essential for debugging and reporting. Playwright supports:
- Screenshots – Full page or element specific.
- PDF generation – Ideal for invoices or reports.
- Video recording – Capture the entire test run.
# Full‑page screenshot
page.screenshot(path="full.png", full_page=True)
# Element screenshot
logo = page.locator("img[alt='Logo']")
logo.screenshot(path="logo.png")
# Save page as PDF (Chromium only)
page.pdf(path="report.pdf", format="A4")
# Record video (needs context option)
context = browser.new_context(record_video_dir="videos/")
page = context.new_page()
page.goto("https://example.com")
page.wait_for_timeout(2000) # Let the page settle
context.close() # Video saved automatically
Handling Multiple Pages, Tabs, and Pop‑ups
Modern web apps frequently open new tabs or windows. Playwright makes it easy to manage them:
# Listen for a new page (tab) event
def on_new_page(page):
print("New page URL:", page.url)
context = browser.new_context()
context.on("page", on_new_page)
page = context.new_page()
page.goto("https://example.com")
page.click("a[target='_blank']") # Opens a new tab
# The callback prints the URL of the newly opened page
For pop‑ups like alerts, confirmations, or prompts, use the page.on("dialog") handler:
def handle_dialog(dialog):
print("Dialog message:", dialog.message)
dialog.accept() # Or dialog.dismiss()
page.on("dialog", handle_dialog)
page.click("button#delete") # Triggers a confirm dialog
Integrating Playwright with Test Frameworks
Most teams combine Playwright with pytest for a clean test runner experience. Below is a minimal conftest.py that provides a browser fixture:
# conftest.py
import pytest
from playwright.sync_api import sync_playwright
@pytest.fixture(scope="session")
def browser():
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
yield browser
browser.close()
# test_login.py
def test_successful_login(browser):
page = browser.new_page()
page.goto("https://example.com/login")
page.fill("input#email", "user@example.com")
page.fill("input#password", "Secret123")
page.click("button#login")
expect(page).to_have_url("**/dashboard")
page.close()
For async projects, replace sync_playwright with async_playwright and use pytest-asyncio to handle the event loop.
Best Practices and Performance Tips
- Reuse browser contexts instead of launching a new browser for each test. Contexts are lightweight and share the same executable.
- Enable tracing for detailed logs:
browser.start_tracing(...)andbrowser.stop_tracing(path="trace.zip"). - Parallel execution: Use
pytest-xdistor Playwright’s built‑in test runner to run multiple workers. - Keep selectors stable: Prefer data attributes (e.g.,
data-test-id) over volatile CSS classes. - Limit network traffic in CI by mocking or blocking unnecessary resources with
page.route().
# Block images and fonts to speed up tests
page.route("**/*.{png,jpg,svg,woff,woff2}", lambda route: route.abort())
Debugging Playwright Scripts
If a test fails, these tools can help you pinpoint the issue:
- Playwright Inspector: Run
pwdebugor setPWDEBUG=1to launch an interactive UI. - Trace Viewer: Open the
.zip
Leave a Reply