The idea
Every Playwright script, from a 5-line test to a scraper that runs every night, starts the same way: start Playwright, launch a browser, open a page. This module covers that skeleton properly, so the rest of the track can focus on the interesting parts.
Installing (on your own computer)
Playwright is two things: a Python package, and the browsers it controls. In a uv project:
uv add playwright
uv run playwright install chromium
The first command adds the package to pyproject.toml; the second downloads a matching Chromium build (about 150 MB, once). In this course, a practice browser is already set up, so the code below runs right here.
The skeleton
Every Playwright script starts the same way: launch a browser, open a tab, go to a page, do your work, and close up. Here's the smallest version, which visits the practice shop and prints the page title:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://nusantara-gadget.test/")
print(page.title())
browser.close()
Nusantara Gadget — Official Gadget Store
Line by line:
sync_playwright()starts Playwright. Thewithblock guarantees it shuts down properly, even if your code crashes halfway.p.chromium.launch()starts a browser.p.firefoxandp.webkit(Safari's engine) work the same way.browser.new_page()opens a tab.page.goto(url)loads a URL and waits for the page'sloadevent.browser.close()closes the browser. Forgetting it leaves browser processes running on your machine.
The sync in sync_playwright means each line waits for its action to finish before moving on, which is exactly what you want for scripts. Playwright also has an async_api for running many pages concurrently; you won't need it until you're scraping at scale.
Headless or headed?
By default the browser is headless: it runs without a visible window. That's faster and what you want on servers. While developing, it's often useful to see what's happening:
with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=500)
page = browser.new_page()
page.goto("https://nusantara-gadget.test/products")
print(page.title())
browser.close()
Products — page 1 — Nusantara Gadget
headless=False opens a real window; slow_mo=500 pauses 500 ms between actions so you can follow along. Switch back to headless once the script works.
Browser, context, page
Playwright has three levels:
| Object | Is like | Holds |
|---|---|---|
Browser | the browser application | one or more contexts |
BrowserContext | a fresh, private browser profile | cookies, storage, settings (viewport, user agent) |
Page | a tab | one URL at a time |
browser.new_page() is a shortcut that creates a context with one page. Create contexts yourself when you need settings, or several independent sessions:
with sync_playwright() as p:
browser = p.chromium.launch()
phone = browser.new_context(viewport={"width": 390, "height": 844})
laptop = browser.new_context(viewport={"width": 1440, "height": 900})
phone_page = phone.new_page()
laptop_page = laptop.new_page()
phone_page.goto("https://nusantara-gadget.test/deals")
laptop_page.goto("https://nusantara-gadget.test/products")
print(phone_page.viewport_size, phone_page.title())
print(laptop_page.viewport_size, laptop_page.title())
browser.close()
{'width': 390, 'height': 844} Flash deals — Nusantara Gadget
{'width': 1440, 'height': 900} Products — page 1 — Nusantara Gadget
Contexts are cheap and isolated: logging in on one doesn't log in the other. That's how you'd scrape as two different users, or start each run with a clean slate.
sync_playwright() → launch() a browser → new_context() for a clean profile → new_page() for a tab → goto(url). Always close the browser, and let the with block clean up Playwright.
Outside a with block
In a notebook or an interactive session you may want the browser to stay open between cells. Use start() and stop() instead of with:
p = sync_playwright().start()
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://nusantara-gadget.test/catalog")
print(page.url)
browser.close()
p.stop()
https://nusantara-gadget.test/catalog
You're now responsible for calling close() and stop() yourself; in scripts, prefer the with form.
Why this matters for clean code
Resource cleanup is the difference between a script that runs for months and one that slowly fills a server with zombie browser processes. Context managers (with) make cleanup automatic, and wrapping the skeleton in a function (as in the exercises) means you write it once and every scraper reuses it.
Recap
- Install with
uv add playwrightanduv run playwright install chromium. with sync_playwright() as p:starts and stops Playwright safely.p.chromium.launch()(headless by default);headless=False, slow_mo=...for debugging.- Browser → context (isolated profile and settings) → page (a tab).
- Close the browser; use
start()/stop()only for interactive sessions.