# Playwright Browser API Reference (Automation Focus) This reference covers Playwright's Python async API for browser automation tasks — NOT testing. For test-specific APIs (assertions, fixtures, test runners), see playwright-pro. ## Browser Launch & Context ### Launching the Browser ```python from playwright.async_api import async_playwright async with async_playwright() as p: # Chromium (recommended for most automation) browser = await p.chromium.launch(headless=True) # Firefox (better for some anti-detection scenarios) browser = await p.firefox.launch(headless=True) # WebKit (Safari engine — useful for Apple-specific sites) browser = await p.webkit.launch(headless=True) ``` **Launch options:** | Option | Type | Default | Purpose | |--------|------|---------|---------| | `headless` | bool | True | Run without visible window | | `slow_mo` | int | 0 | Milliseconds to slow each operation (debugging) | | `proxy` | dict | None | Proxy server configuration | | `args` | list | [] | Additional Chromium flags | | `downloads_path` | str | None | Directory for downloads | | `channel` | str | None | Browser channel: "chrome", "msedge" | ### Browser Contexts (Session Isolation) Browser contexts are isolated environments within a single browser instance. Each context has its own cookies, localStorage, and cache. Use them instead of launching multiple browsers. ```python # Create isolated context context = await browser.new_context( viewport={"width": 1920, "height": 1080}, user_agent="Mozilla/5.0 ...", locale="en-US", timezone_id="America/New_York", geolocation={"latitude": 40.7128, "longitude": -74.0060}, permissions=["geolocation"], ) # Multiple contexts share one browser (resource efficient) context_a = await browser.new_context() # User A session context_b = await browser.new_context() # User B session ``` ### Storage State (Session Persistence) ```python # Save state after login (cookies + localStorage) await context.storage_state(path="auth_state.json") # Restore state in new context context = await browser.new_context(storage_state="auth_state.json") ``` ## Page Navigation ### Basic Navigation ```python page = await context.new_page() # Navigate with different wait strategies await page.goto("https://example.com") # Default: "load" await page.goto("https://example.com", wait_until="domcontentloaded") # Faster await page.goto("https://example.com", wait_until="networkidle") # Wait for network quiet await page.goto("https://example.com", timeout=30000) # Custom timeout (ms) ``` **`wait_until` options:** - `"load"` — wait for the `load` event (all resources loaded) - `"domcontentloaded"` — DOM is ready, images/styles may still load - `"networkidle"` — no network requests for 500ms (best for SPAs) - `"commit"` — response received, before any rendering ### Wait Strategies ```python # Wait for a specific element to appear await page.wait_for_selector("div.content", state="visible") await page.wait_for_selector("div.loading", state="hidden") # Wait for loading to finish await page.wait_for_selector("table tbody tr", state="attached") # In DOM but maybe not visible # Wait for URL change await page.wait_for_url("**/dashboard**") await page.wait_for_url(re.compile(r"/dashboard/\d+")) # Wait for specific network response async with page.expect_response("**/api/data*") as resp_info: await page.click("button.load") response = await resp_info.value json_data = await response.json() # Wait for page load state await page.wait_for_load_state("networkidle") # Fixed wait (use sparingly — prefer the methods above) await page.wait_for_timeout(1000) # milliseconds ``` ### Navigation History ```python await page.go_back() await page.go_forward() await page.reload() ``` ## Element Interaction ### Finding Elements ```python # Single element (returns first match) element = await page.query_selector("css=div.product") element = await page.query_selector("xpath=//div[@class='product']") # Multiple elements elements = await page.query_selector_all("div.product") # Locator API (recommended — auto-waits, re-queries on each action) locator = page.locator("div.product") count = await locator.count() first = locator.first nth = locator.nth(2) ``` **Locator vs query_selector:** - `query_selector` — returns an ElementHandle at a point in time. Can go stale if DOM changes. - `locator` — returns a Locator that re-queries each time you interact with it. Preferred for reliability. ### Clicking ```python await page.click("button.submit") await page.click("a:has-text('Next')") await page.dblclick("div.editable") await page.click("button", position={"x": 10, "y": 10}) # Click at offset await page.click("button", force=True) # Skip actionability checks await page.click("button", modifiers=["Shift"]) # With modifier key ``` ### Text Input ```python # Fill (clears existing content first) await page.fill("input#email", "user@example.com") # Type (simulates keystroke-by-keystroke input — slower, more realistic) await page.type("input#search", "query text", delay=50) # 50ms between keys # Press specific keys await page.press("input#search", "Enter") await page.press("body", "Control+a") ``` ### Dropdowns & Select ```python # Native