Learning Record learning from practice

archive node

Handling Pages That Require Login with Playwright

Handling Pages That Require Login with Playwright

When using Playwright to convert web pages to PDF, wait_until="networkidle" waits for all network requests to settle. However, if the target page requires authentication, the browser may hang indefinitely or get redirected to a login page.

This post covers two practical approaches to handle login-protected pages.


Run a script once to log in manually, save the cookies and storage state, then reuse it for subsequent requests:

from playwright.sync_api import sync_playwright
import json, os

STORAGE_FILE = "auth_state.json"

def save_login_state(login_url: str):
    """Run once: log in manually and save the session."""
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)  # Launch with UI for manual login
        context = browser.new_context()
        page = context.new_page()
        page.goto(login_url)
        
        # Wait for manual login (press Enter in terminal when done)
        print("Please complete the login in the browser, then press Enter to continue...")
        input()
        
        # Save authentication state (cookies + localStorage)
        context.storage_state(path=STORAGE_FILE)
        print(f"Login state saved to {STORAGE_FILE}")
        browser.close()

def webpage_to_pdf(url: str, output_path: str):
    with sync_playwright() as p:
        browser = p.chromium.launch()
        
        # Load saved login state if available
        if os.path.exists(STORAGE_FILE):
            context = browser.new_context(storage_state=STORAGE_FILE)
        else:
            context = browser.new_context()
        
        page = context.new_page()

        # Set a timeout to avoid waiting forever
        page.goto(url, wait_until="networkidle", timeout=30000)
        
        # Check if we were redirected to a login page
        if "login" in page.url or "signin" in page.url:
            print("Authentication required. Please run save_login_state() first.")
            browser.close()
            return

        page.pdf(
            path=output_path,
            format="A4",
            print_background=True,
            margin={"top": "20mm", "bottom": "20mm", "left": "15mm", "right": "15mm"}
        )

        browser.close()
        print(f"PDF saved to {output_path}")

# First time: save login state
# save_login_state("https://example.com/login")

# Subsequent runs: convert page to PDF
webpage_to_pdf(
    "https://example.com",
    "output.pdf"
)

How it works:

  1. save_login_state() launches a visible browser where you log in manually.
  2. After login, context.storage_state() persists all cookies and localStorage to a JSON file.
  3. webpage_to_pdf() loads that saved state into a new browser context, so the server sees you as already authenticated.

Approach 2: Programmatic Login via Form Submission

If the site uses a standard username/password form, you can automate the login entirely:

def login_and_save_pdf(login_url: str, target_url: str, output_path: str,
                       username: str, password: str):
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page()
        
        # Navigate to login page
        page.goto(login_url)
        page.fill('input[name="username"]', username)  # Adjust selector as needed
        page.fill('input[name="password"]', password)
        page.click('button[type="submit"]')
        
        # Wait for login to complete (wait for a post-login URL or element)
        page.wait_for_url("**/dashboard**")  # Or use wait_for_selector()
        
        # Navigate to the target page
        page.goto(target_url, wait_until="networkidle", timeout=30000)
        page.pdf(path=output_path, format="A4", print_background=True)
        
        browser.close()

# Example usage
login_and_save_pdf(
    login_url="https://example.com/login",
    target_url="https://example.com",
    output_path="output.pdf",
    username="user@example.com",
    password="your-password"
)

When to use this: Works well for simple form-based logins. Not suitable for sites that use OAuth, SSO, CAPTCHA, or multi-factor authentication — use Approach 1 for those.


First run  → save_login_state()  → Log in manually  → Saves auth_state.json
Later runs → Auto-loads auth_state.json → Directly accesses the target page

Warning: auth_state.json contains sensitive cookies and tokens. Add it to .gitignore and never commit it to version control.