Building Browser-Using AI Agents in Python A new tutorial teaches developers how to build AI agents that browse and interact with real websites using Playwright, browser-use, and LangGraph, addressing the gap where most tasks lack APIs. The global AI agents market is projected to reach $50.31 billion by 2030, with browser-capable agents driving growth as 27.7% of enterprises already use agentic browsers in production. In this article, you will learn how to build AI agents that can browse and interact with real websites using Playwright, browser-use, and LangGraph. Topics we will cover include: - Why Playwright is the right foundation for browser automation in 2026, and how it differs from Selenium. - How to scrape dynamic, JavaScript-rendered pages and complete multi-step forms reliably. - How to wire browser actions into LangGraph and browser-use agents, handle anti-bot detection, manage waiting and session persistence, and deploy the result in Docker. Introduction Most AI agent tutorials start with an API. They show you how to call OpenWeather, hit the Stripe endpoint, pull data from GitHub. That is a fine starting point until you try to build something real and realize that the task you actually need done does not have an API. Think about what humans do with browsers every day: filing government forms, reading competitor pricing, extracting research from sites that guard their data behind JavaScript rendering, logging into portals that have never heard of OAuth. There are roughly 1.1 billion websites on the internet. A vanishingly small fraction of them have public APIs. The rest only speak browser. An agent that is limited to API calls handles maybe 5% of the tasks a human worker does daily. Give that agent a browser, and the coverage approaches everything. That is the gap this article closes. The global AI agents market https://www.ringly.io/blog/ai-agent-statistics-2026 stands at \$10.91 billion in 2026 and is projected to reach \$50.31 billion by 2030, with browser-capable agents at the center of that growth. 27.7% of enterprises https://velofill.com/articles/agentic-ai-browsers-2026-market-landscape/ are already running agentic browsers in production, up from virtually none two years prior. The tooling has matured fast, and the patterns are settled enough to teach properly. By the end of this article, you will have a working browser agent that navigates real websites, fills forms, extracts structured data, and connects to an LLM that decides what to do next, all in Python. Why Playwright, Not Selenium If you built browser automation five years ago, you built it with Selenium. Selenium is still widely deployed, still works, and is not going anywhere. But for any new project in 2026, Playwright is the default. The reasons are practical, not theoretical. Selenium communicates with the browser by sending individual HTTP requests to a WebDriver. Every action, click, type, scroll, is a separate request. Playwright uses a persistent WebSocket connection for the entire session. Commands flow through that channel with no per-action round-trip cost. Independent benchmarks consistently show Playwright running 30-50% faster than Selenium at the test-suite level and averaging ~290ms per action versus Selenium’s ~536ms. For a browser agent that might execute hundreds of actions, that gap compounds. Playwright also bundles its own browser binaries. When you install it, you get pre-configured versions of Chromium, Firefox, and WebKit that are guaranteed to work with your Playwright version. No driver version mismatches, no broken CI pipelines because someone updated Chrome. It has built-in auto-waiting before it clicks an element; it verifies the element is visible, enabled, and not animating. You do not have to write time.sleep 2 and hope for the best. For AI agents specifically, Playwright fires real mouse and keyboard events that mirror how humans interact with browsers. Sites designed to detect automation look for synthetic DOM clicks. Playwright’s interaction model is harder to distinguish from genuine human input. There is also the browser-use library, which sits one level higher. Browser-use is a Python library that gives an LLM a working browser. Under the hood, it uses Playwright to drive the browser, but the LLM reads the page state and decides what to click, type, and extract, no CSS selectors required. You give it a task in plain English, and it figures out the rest. We will cover both raw Playwright and browser-use in this article, because they serve different needs: Playwright when you want precise, predictable control; browser-use when you want the agent to handle navigation decisions autonomously. Setting Up the Environment You need Python 3.10 or higher, an OpenAI API key, and about five minutes. Step 1: Create a virtual environment python -m venv browser agent env macOS / Linux source browser agent env/bin/activate Windows browser agent env\Scripts\activate 1234567 python -m venv browser agent env macOS / Linuxsource browser agent env/bin/activate Windowsbrowser agent env\Scripts\activate Step 2: Install dependencies pip install playwright \ browser-use \ langchain \ langchain-openai \ langgraph \ langchain-community \ python-dotenv 1234567 pip install playwright \ browser-use \ langchain \ langchain-openai \ langgraph \ langchain-community \ python-dotenv Step 3: Install the browser binaries This is the step most people miss. Playwright needs to download Chromium, Firefox, and WebKit separately from the Python package. Run this once after installing: playwright install chromium 1 playwright install chromium If you want all three browser engines: playwright install . Chromium alone is sufficient for most agent work and is smaller to download. Step 4: Store your API key Create a .env file in your project directory: OPENAI API KEY=your openai api key here 1 OPENAI API KEY=your openai api key here Add .env to your .gitignore immediately. Do not commit API keys. Step 5: Verify everything works Here is a first script that navigates to a URL, reads the heading, and saves a screenshot. Use example.com http://example.com , a publicly available test domain maintained by IANA that will not block you. How to run: Save as first run.py and run python first run.py first run.py Navigate to a URL, take a screenshot, and extract the page title. Prerequisites: pip install playwright && playwright install chromium How to run: python first run.py import asyncio from playwright.async api import async playwright async def main : async with async playwright as p: Launch Chromium in headless mode no visible browser window . Set headless=False if you want to watch it run during development. browser = await p.chromium.launch headless=True A browser context is like a fresh browser profile. It isolates cookies, storage, and cache from other contexts. context = await browser.new context viewport={"width": 1280, "height": 720}, user agent= "Mozilla/5.0 Windows NT 10.0; Win64; x64 " "AppleWebKit/537.36 KHTML, like Gecko " "Chrome/120.0.0.0 Safari/537.36" page = await context.new page Navigate to the URL and wait until the network is idle. "networkidle" means no open network connections for 500ms. For faster pages, "domcontentloaded" is sufficient. await page.goto "https://example.com", wait until="networkidle" Extract the page title title = await page.title print f"Page title: {title}" Extract the text content of the h1 heading h1 = await page.text content "h1" print f"H1 heading: {h1}" Take a full-page screenshot and save it to disk await page.screenshot path="screenshot.png", full page=True print "Screenshot saved to screenshot.png" await browser.close asyncio.run main 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 first run.py Navigate to a URL, take a screenshot, and extract the page title. Prerequisites: pip install playwright && playwright install chromium How to run: python first run.py import asynciofrom playwright.async api import async playwright async def main : async with async playwright as p: Launch Chromium in headless mode no visible browser window . Set headless=False if you want to watch it run during development. browser = await p.chromium.launch headless=True A browser context is like a fresh browser profile. It isolates cookies, storage, and cache from other contexts. context = await browser.new context viewport={"width": 1280, "height": 720}, user agent= "Mozilla/5.0 Windows NT 10.0; Win64; x64 " "AppleWebKit/537.36 KHTML, like Gecko " "Chrome/120.0.0.0 Safari/537.36" page = await context.new page Navigate to the URL and wait until the network is idle. "networkidle" means no open network connections for 500ms. For faster pages, "domcontentloaded" is sufficient. await page.goto "https://example.com", wait until="networkidle" Extract the page title title = await page.title print f"Page title: {title}" Extract the text content of the h1 heading h1 = await page.text content "h1" print f"H1 heading: {h1}" Take a full-page screenshot and save it to disk await page.screenshot path="screenshot.png", full page=True print "Screenshot saved to screenshot.png" await browser.close asyncio.run main What this does: async playwright is the entry point for the entire Playwright session. The browser context is equivalent to opening a fresh incognito window; cookies, local storage, and cache are isolated from everything else. wait until=”networkidle” tells Playwright to wait until the page has finished all its network activity before your code continues, which is the safest wait strategy for dynamic pages. If this runs and saves a screenshot, your environment is working correctly. Web Navigation and Scraping The reason you need Playwright instead of requests + BeautifulSoup is JavaScript rendering. Modern websites deliver a skeleton of HTML and then build the actual content dynamically after the page loads: React, Vue, Angular, Next.js. A plain HTTP request fetches the skeleton. Playwright runs a real browser, so it sees exactly what a human sees after all JavaScript has executed. The target below is books.toscrape.com http://books.toscrape.com/ , a legal scraping sandbox built for practice. It paginates results, uses dynamic class names for ratings, and closely mirrors the structure of real e-commerce product pages. How to run: Save as scrape books.py and run python scrape books.py scrape books.py Scrape book titles, prices, and ratings from books.toscrape.com This is a legal scraping sandbox site built for practice. Prerequisites: pip install playwright && playwright install chromium How to run: python scrape books.py import asyncio import json from playwright.async api import async playwright async def scrape books max pages: int = 3 - list dict : """ Scrape book listings from books.toscrape.com across multiple pages. Returns a list of dicts with title, price, rating, and page number. """ results = async with async playwright as p: browser = await p.chromium.launch headless=True context = await browser.new context viewport={"width": 1280, "height": 720} page = await context.new page for page num in range 1, max pages + 1 : url = f"https://books.toscrape.com/catalogue/page-{page num}.html" print f"Scraping page {page num}: {url}" await page.goto url, wait until="domcontentloaded" Wait for the product cards to be visible before extracting. This is critical on JavaScript-heavy pages where content loads after the HTML. timeout=10000 means wait up to 10 seconds before raising an error. await page.wait for selector "article.product pod", timeout=10000 Get all book cards on the current page books = await page.query selector all "article.product pod" for book in books: Extract title from the