Playwright Automation with Python: Simplicity Meets Power
November 18, 2024
Playwright Automation with Python: Simplicity Meets Power
Python's simplicity combined with Playwright's capabilities creates an incredibly productive automation toolkit. Let's dive into how to harness this powerful combination.
Why Playwright with Python?
Python developers love Playwright because:
- Pythonic API: Natural, intuitive syntax
- Async Support: Modern async/await patterns
- Easy Setup: Simple pip installation
- Rich Ecosystem: Integrates with pytest, unittest, and more
Installation and Setup
Getting started is straightforward:
bash1pip install playwright 2playwright install
This installs Playwright and downloads the necessary browser binaries.
Your First Python Script
Here's a simple web scraping example:
python1from playwright.sync_api import sync_playwright 2 3with sync_playwright() as p: 4 browser = p.chromium.launch() 5 page = browser.new_page() 6 page.goto('https://example.com') 7 8 # Extract data 9 title = page.title() 10 heading = page.locator('h1').inner_text() 11 12 print(f"Title: {title}") 13 print(f"Heading: {heading}") 14 15 browser.close()
Async vs Sync API
Playwright offers both synchronous and asynchronous APIs:
Synchronous (Easier for Beginners)
python1from playwright.sync_api import sync_playwright 2 3def test_homepage(): 4 with sync_playwright() as p: 5 browser = p.chromium.launch() 6 page = browser.new_page() 7 page.goto('https://example.com') 8 assert 'Example' in page.title() 9 browser.close()
Asynchronous (Better Performance)
python1import asyncio 2from playwright.async_api import async_playwright 3 4async def test_homepage(): 5 async with async_playwright() as p: 6 browser = await p.chromium.launch() 7 page = await browser.new_page() 8 await page.goto('https://example.com') 9 title = await page.title() 10 assert 'Example' in title 11 await browser.close() 12 13asyncio.run(test_homepage())
Pytest Integration
Playwright integrates seamlessly with pytest:
bash1pip install pytest-playwright
python1# test_example.py 2import pytest 3from playwright.sync_api import Page 4 5def test_homepage_title(page: Page): 6 page.goto('https://example.com') 7 assert 'Example Domain' in page.title() 8 9def test_navigation(page: Page): 10 page.goto('https://example.com') 11 page.click('a:has-text("More information")') 12 page.wait_for_url('**/iana.org/**')
Run tests with:
bash1pytest --headed --browser chromium
Web Scraping with Python
A practical data extraction example:
python1from playwright.sync_api import sync_playwright 2import json 3 4def scrape_products(): 5 with sync_playwright() as p: 6 browser = p.chromium.launch(headless=True) 7 page = browser.new_page() 8 page.goto('https://example-shop.com/products') 9 10 # Wait for products to load 11 page.wait_for_selector('.product-card') 12 13 # Extract product data 14 products = [] 15 for product in page.locator('.product-card').all(): 16 products.append({ 17 'name': product.locator('.product-name').inner_text(), 18 'price': product.locator('.product-price').inner_text(), 19 'image': product.locator('img').get_attribute('src') 20 }) 21 22 browser.close() 23 return products 24 25# Save to JSON 26products = scrape_products() 27with open('products.json', 'w') as f: 28 json.dump(products, f, indent=2)
Advanced Testing Patterns
Page Object Model
python1class LoginPage: 2 def __init__(self, page: Page): 3 self.page = page 4 self.email_input = page.locator('#email') 5 self.password_input = page.locator('#password') 6 self.submit_button = page.locator('button[type="submit"]') 7 8 def login(self, email: str, password: str): 9 self.email_input.fill(email) 10 self.password_input.fill(password) 11 self.submit_button.click() 12 13 def expect_error(self, message: str): 14 error = self.page.locator('.error-message') 15 assert error.inner_text() == message 16 17# Usage in test 18def test_invalid_login(page: Page): 19 login_page = LoginPage(page) 20 login_page.login('invalid@email.com', 'wrongpass') 21 login_page.expect_error('Invalid credentials')
Fixtures and Reusable Components
python1import pytest 2from playwright.sync_api import Page 3 4@pytest.fixture 5def authenticated_page(page: Page): 6 """Fixture that provides an authenticated session""" 7 page.goto('https://example.com/login') 8 page.fill('#email', 'test@example.com') 9 page.fill('#password', 'password123') 10 page.click('button:has-text("Login")') 11 page.wait_for_url('**/dashboard') 12 return page 13 14def test_dashboard_access(authenticated_page: Page): 15 assert authenticated_page.locator('.welcome-message').is_visible()
Handling Dynamic Content
Waiting for Elements
python1# Wait for specific selector 2page.wait_for_selector('.data-loaded') 3 4# Wait for network idle 5page.goto('https://example.com', wait_until='networkidle') 6 7# Custom wait condition 8page.wait_for_function('document.querySelectorAll(".item").length > 10')
Intercepting Network Requests
python1def test_api_mock(page: Page): 2 # Mock API response 3 def handle_route(route): 4 route.fulfill( 5 status=200, 6 body=json.dumps({'user': 'Test User', 'role': 'admin'}) 7 ) 8 9 page.route('**/api/user', handle_route) 10 page.goto('https://example.com/profile') 11 assert page.locator('.username').inner_text() == 'Test User'
Taking Screenshots and Videos
python1def test_visual_regression(page: Page): 2 page.goto('https://example.com') 3 4 # Full page screenshot 5 page.screenshot(path='homepage.png', full_page=True) 6 7 # Element screenshot 8 page.locator('.hero-section').screenshot(path='hero.png') 9 10# Record video (in pytest.ini or conftest.py) 11@pytest.fixture(scope='session') 12def browser_context_args(browser_context_args): 13 return { 14 **browser_context_args, 15 'record_video_dir': 'videos/' 16 }
CI/CD Integration
GitHub Actions Example
yaml1name: Playwright Tests 2on: [push, pull_request] 3jobs: 4 test: 5 runs-on: ubuntu-latest 6 steps: 7 - uses: actions/checkout@v3 8 - uses: actions/setup-python@v4 9 with: 10 python-version: '3.11' 11 - name: Install dependencies 12 run: | 13 pip install -r requirements.txt 14 playwright install --with-deps 15 - name: Run tests 16 run: pytest --browser chromium --headed=false
Best Practices
1. Use Context Managers
python1# Always use 'with' statement 2with sync_playwright() as p: 3 # Your code here 4 pass # Browser automatically closes
2. Leverage Auto-Waiting
python1# Playwright waits automatically 2page.click('button') # Waits for button to be clickable 3page.fill('input', 'text') # Waits for input to be editable
3. Use Data Classes for Test Data
python1from dataclasses import dataclass 2 3@dataclass 4class User: 5 email: str 6 password: str 7 role: str 8 9admin_user = User('admin@test.com', 'admin123', 'admin')
Debugging Tips
python1# Add pause for debugging 2page.pause() 3 4# Enable verbose logging 5import logging 6logging.basicConfig(level=logging.DEBUG) 7 8# Slow down execution 9browser = p.chromium.launch(slow_mo=1000) # 1 second delay
Conclusion
Playwright with Python offers:
ā Simple, readable code ā Powerful automation capabilities ā Excellent pytest integration ā Great for both testing and scraping
Whether you're automating tests, scraping data, or building bots, Playwright's Python API makes it accessible and productive.
Happy coding! šš
Was this helpful?
0
0
0
Comments (0)
Join the Discussion
Sign in to share your thoughts and connect with other readers
Related Articles
Playwright Automation with TypeScript: A Modern Approach
Discover how to build robust end-to-end test automation using Playwright and TypeScript. Learn best practices, type safety benefits, and real-world implementation strategies.
Selenium with Python: Complete Automation Guide
Master web automation with Selenium and Python. Learn from basics to advanced techniques including waits, page objects, and parallel testing strategies.
Getting Started with Playwright Automation: A Beginner's Guide
New to test automation? Learn the fundamentals of Playwright from installation to writing your first automated tests. Perfect for beginners starting their automation journey.