Web Automation

From PRIMO.ai
Jump to navigation Jump to search

YouTube search... ...Google search

Web automation means getting software to do what a person would otherwise do in a web browser: fetch pages, read and extract information, fill in and submit forms, click through multi-step workflows, check that a site behaves correctly, and move data between websites and the tools where it is actually used, such as spreadsheets and databases. It covers everything from a one-line bookmarklet that reformats the page in front of you, to a nightly scraper running in the cloud, to an AI agent that books travel across several sites while you watch.

The core problem has not changed in thirty years. The web was built for human eyes and hands, not for programs. Pages mix content with layout, important data often appears only after JavaScript runs, many useful services offer no API, and websites change without warning. Every generation of web automation is a different answer to the same question: how does a program reliably find the right thing on a page, act on it, and confirm that the action worked?

The answers have arrived in layers:

  • HTTP and parsing (1990s onward). Scripts request a URL and parse the returned HTML. Leonard Richardson's Beautiful Soup (2004) and the Requests library made this approachable in Python. Excel web queries, Power Query, and VBA's XML HTTP objects brought the same idea to spreadsheets. Scrapy (first released in 2008 by developers who went on to found Zyte) industrialized crawling.
  • In-page scripting (late 1990s onward). Bookmarklets, which are bookmarks whose address starts with javascript:, run small programs inside the page you are viewing. Greasemonkey (2005) introduced userscripts that run automatically on matching sites; Tampermonkey and Violentmonkey carry that tradition forward.
  • Browser drivers (2004 onward). Selenium began at ThoughtWorks in 2004 (Jason Huggins), merged with Simon Stewart's WebDriver, and became the W3C WebDriver standard in 2018. Google's Puppeteer (2017) and Microsoft's Playwright (2020) drive browsers through the Chrome DevTools Protocol and related protocols, with auto-waiting, network interception, and cross-browser support.
  • Workflow and RPA tools. Power Automate, UiPath, n8n, Zapier, and similar tools wrap browser and API steps into business workflows that non-developers can maintain.
  • AI agents (2024 onward). Large language models can now look at a page (through its HTML, its accessibility tree, or a screenshot), decide what to do next, and act. Anthropic's computer use (October 2024), the Model Context Protocol (MCP) (November 2024), OpenAI's Operator (January 2025), and a wave of agentic browsers in 2025 and 2026 turned this from a research demo into everyday tooling.
  • Agent-ready websites (2026). WebMCP, an experimental browser API now in a Chrome origin trial, flips the model: instead of agents guessing how a site works, the site declares its own actions as structured tools.

Two shifts define the current moment. First, chatbots made scripting accessible. Someone who has never written JavaScript can describe a task to ChatGPT, Claude, or Gemini and get a working bookmarklet, userscript, or Python scraper in minutes. Second, automation became probabilistic. A classic Selenium script either finds its selector or fails loudly. An AI agent adapts to page changes and unfamiliar sites, but it can also misread the page, click the wrong thing, or be manipulated by instructions hidden in content it reads. The most reliable systems in 2026 combine both: AI explores and handles variation, while deterministic code replays the parts that are already understood.



Web automation is moving from scripts that imitate clicks toward agents that negotiate with websites. The durable skill is the same as it has always been: know which layer you are automating (HTTP, the DOM, the browser, or the agent), verify every step, and stay inside the permissions you actually have.



Mechanisms

Every web automation, from a bookmarklet to an autonomous agent, runs some version of the same loop:

  1. Locate the target: a URL, an element, a table, a form field, or a tool.
  2. Act on it: request, click, type, select, submit, or call.
  3. Wait for the page or server to respond. This is where most flaky automation fails.
  4. Verify that the intended effect happened, preferably through a different channel than the action itself (for example, check the network response or the updated page rather than trusting that a click "succeeded").
  5. Extract the result and store it somewhere useful.
  6. Repeat or recover: move to the next page, retry with backoff, or stop and ask a human.

What changes from one tool to the next is the layer at which this loop operates.

Layer What it sees Typical tools Strengths Weaknesses
HTTP Raw responses (HTML, JSON, CSV) Requests, httpx, fetch, Power Query Web.Contents, VBA MSXML2.XMLHTTP Fast, cheap, easy to schedule Misses content rendered by JavaScript; no real browser session
Parsing A tree of HTML elements Beautiful Soup, lxml, CSS selectors, XPath, regular expressions Precise extraction Breaks when markup changes
Browser control A live, rendered page Selenium, Playwright, Puppeteer, SeleniumVBA Handles JavaScript, logins, downloads, real user flows Heavier, slower, more detectable
In-page scripting The DOM from inside the page Bookmarklets, userscripts, extensions, DevTools snippets Runs in your own logged-in session with zero setup Limited by page security policies; manual trigger (bookmarklets)
Agentic DOM, accessibility tree, screenshots, plus a goal in plain language Claude in Chrome, Gemini in Chrome, Comet, ChatGPT agent, browser-use, Stagehand, Playwright MCP Adapts to unfamiliar or changing pages Costly per step, probabilistic, vulnerable to prompt injection
Site-declared tools Named actions with JSON schemas WebMCP (experimental), public APIs, MCP servers Reliable and efficient when the site cooperates Requires the site to opt in

How an AI browser agent works. An agent wraps a language model in a harness that repeats an observe, decide, act cycle:

  • Observe. The harness gives the model a representation of the page. Text-based agents receive a trimmed DOM or an accessibility snapshot (roles, names, and values of interactive elements, often with short reference IDs). Vision-based agents receive a screenshot. Many current systems combine both, because each channel misses things the other catches: a screenshot shows text baked into images, while an accessibility snapshot exposes a field's exact value and role.
  • Decide. The model chooses the next action, such as "click element e42" or "type 'Boston' into the destination field," based on the user's goal and the history so far.
  • Act. The harness executes the action through a browser automation layer (usually the Chrome DevTools Protocol, Playwright, or an extension API) and captures the new state.
  • Verify and continue. The loop repeats until the goal is met, a step limit is reached, or the agent hands control back to the user, for example before a purchase or when it encounters a login it should not complete on its own.

How MCP fits in. The Model Context Protocol (MCP) is a standard way for an AI application to discover and call external tools. A browser MCP server, such as Playwright MCP or Chrome DevTools MCP, exposes actions like browser_navigate, browser_click, or performance_start_trace to any MCP-capable client (Claude Code, Cursor, VS Code, Gemini CLI, and others). The model does not need to know Playwright; it calls tools, and the server translates those calls into browser commands.

The hybrid pattern. A strong consensus has formed among practitioners in 2025 and 2026: use the model where judgment is needed and code everywhere else. Explore a workflow once with an agent, record what worked, and turn the stable steps into a script or reusable "skill." Stagehand caches AI-resolved actions for replay; Playwright's test agents generate ordinary test files; and many teams use AI coding assistants to write deterministic Playwright or Python code rather than letting an agent click through the same flow every day.

Dependencies and conditions. Automation depends on things you do not control: the site's markup, its JavaScript framework, its bot defenses, its terms of service, your account's permissions, and network conditions. Agentic automation adds model behavior, model cost, and the risk that page content will try to steer the agent. These dependencies are why verification and human checkpoints matter more than raw capability.

Where uncertainty remains. Nobody yet knows how far agents can push reliability on long, high-stakes tasks, whether websites will widely adopt declared tools like WebMCP, or how courts and infrastructure providers will ultimately treat AI agents that act with a user's credentials. These open questions are covered in What Remains Uncertain and Recent Developments.

Why This Matters

For individuals. Web automation used to require programming skill. In 2025 and 2026 that barrier largely disappeared for small tasks. Writers, researchers, analysts, and administrators now ask a chatbot for a bookmarklet that exports a table, a userscript that hides distractions on a work portal, or a Python script that collects public data, and they get working code in minutes. The constraint has moved from "can I write this?" to "do I understand what this code does, and am I allowed to run it here?"

For engineering. Browser automation is how modern teams test web applications, and it is now how AI coding agents check their own work. Tools like Playwright MCP and Chrome DevTools MCP let a coding agent open the page it just changed, read console errors, run a performance trace, and verify the fix. Playwright's planner, generator, and healer test agents draft and repair end-to-end tests. This closes a long-standing gap: models could write code but could not see what that code did in a real browser.

For organizations. Large amounts of work still happen in portals without usable APIs: government filings, vendor dashboards, insurance and healthcare systems, legacy internal tools. Browser automation and AI agents can bridge these systems using the access an employee already has. The same capability creates risk if agents carry broad, ambient authority from a user's logged-in browser profile.

For the web itself. Automated traffic is now a large and growing share of all requests, and Cloudflare has stated that bots and agents account for more than half of web requests. That changes the economics of publishing. Sites are moving from simple allow-or-block decisions toward verified bot identity, paid crawl access, and, through WebMCP, explicit agent-facing interfaces. Good accessibility practice, meaning semantic HTML and correctly labeled controls, now helps both screen-reader users and AI agents, because agents increasingly navigate through the accessibility tree.

For security. Agentic browsers are a genuine change in browser security. A traditional browser renders untrusted content for a human to judge. An agentic browser reads untrusted content on the human's behalf and can act on it, using the human's sessions. Researchers in 2026 have repeatedly shown that hidden instructions on a web page can redirect agents to leak data or take unwanted actions, and that some agentic designs weaken the same-origin protections the web has relied on since 1995.

Limitations to keep in mind.

  • Scraped data is only as good as the page it came from, and pages change without notice.
  • AI agents remain markedly less reliable on realistic live-web tasks than their marketing suggests; independent benchmarks show large gaps between vendor claims and careful evaluation.
  • Bot defenses, rate limits, and legal terms constrain what is permissible, regardless of what is technically possible.
  • Automation that acts (submits, purchases, deletes, sends) needs stronger safeguards than automation that only reads.

What would change if the agentic web succeeds. If declared tools (WebMCP and MCP), verified agent identity, and robust prompt-injection defenses mature together, automation would shift from brittle imitation of human clicks to something closer to calling an API through the browser, with the human approving consequential steps. If they do not mature, the likely outcome is an arms race between agents and bot defenses, with more sites locking content behind logins, payments, or blocks.

What Remains Uncertain

  • Reliability ceiling. Agents improve quickly, yet compounding errors over long tasks remain the main obstacle. It is unclear how much better models alone can do versus better harnesses, verification, and site cooperation.
  • Prompt injection. Vendors report large reductions in attack success, but researchers continue to find new techniques, and OpenAI has said publicly that prompt injection in agentic browsing may never be fully solved. Whether layered defenses are sufficient for high-stakes work is an open question.
  • Standards adoption. WebMCP is an origin trial in one browser engine, its API shape has already changed once, and other engines have not committed to it.
  • Law and policy. Courts are only beginning to decide when an AI agent acting with a user's credentials counts as the user, and when it counts as a third party accessing a site without authorization.
  • Access economics. Paid crawling, signed agents, and default blocking of AI crawlers may favor large, well-known operators over individuals and small research projects.

State of the Field

Recent Developments

This section focuses on roughly October 2024 through October 2026. Vendor-reported figures are labeled as such; independent research is identified by its authors.

Agentic browsers arrived, consolidated, and were tested hard

  • OpenAI launched Operator in January 2025, folded it into ChatGPT agent in July 2025, and released the standalone ChatGPT Atlas browser for macOS on October 21, 2025. On July 9, 2026, OpenAI announced Atlas's retirement; it stopped working on August 9, 2026. Its agentic browsing moved into a redesigned ChatGPT desktop app (which also absorbed Codex and the new ChatGPT Work agent), a server-side cloud browser, and a Chrome extension. Users had to export Atlas data manually before shutdown.
  • Google rolled out Gemini in Chrome and, in late January 2026, an "auto browse" agent mode powered by Gemini 3 for paying AI Pro and Ultra subscribers in the United States first, followed by Android support on selected phones in mid-2026. Google says Gemini in Chrome will support WebMCP.
  • Anthropic piloted Claude in Chrome in August 2025 and made it generally available to all paid plans on August 26, 2026. The GA release lets Claude act without step-by-step approval, using a classifier that checks each proposed action against the user's request, plus probes that scan page content for injected instructions. Anthropic also added a built-in browser to its desktop app the same day. Enterprise admins can restrict Claude in Chrome to approved domains.
  • Perplexity Comet, a standalone Chromium-based browser with a built-in agent, became one of the most widely used agentic browsers and one of the most frequently attacked by security researchers.
  • Microsoft rebuilt Copilot into Edge with multi-tab and agentic modes; Opera Neon and other AI-native browsers also launched.

The pattern over two years: standalone AI browsers proved harder to sustain than agents embedded in browsers people already use. Atlas's retirement and the growth of extension-based agents (Claude in Chrome, ChatGPT's Chrome extension) and built-in agents (Gemini in Chrome, Copilot in Edge) point the same direction.

Protocols and tooling for agents

  • Model Context Protocol (MCP). Introduced by Anthropic in November 2024, MCP became the default way to connect AI applications to tools. For browser work, it means one browser server can serve many different AI clients.
  • Playwright MCP (Microsoft, first public release in spring 2025) exposes Playwright as MCP tools that work mainly from accessibility snapshots rather than screenshots. It supports persistent profiles, isolated sessions, and an extension mode that connects to existing tabs. Its documentation states plainly that it is not a security boundary.
  • Playwright 1.56 (October 2025) introduced test agents: a planner that explores an app and writes a Markdown test plan, a generator that turns the plan into test files, and a healer that runs and repairs failing tests. npx playwright init-agents sets them up for VS Code, Claude Code, or opencode.
  • Playwright 1.59 (spring 2026) added a Screencast API for recording annotated "video receipts" of agent work, real-time frame streaming for vision models, browser.bind() so multiple clients (scripts, playwright-cli, and MCP servers) can share one running browser, and CLI tools that let coding agents debug tests and inspect traces.
  • Playwright 1.62 (July 24, 2026) bundled the MCP server and playwright-cli into the core package, runnable as npx playwright mcp and npx playwright cli, and isolated the clipboard from the operating system in headless mode.
  • Playwright 1.64 added page.webmcp for listing and calling tools that a page registers through WebMCP, and Playwright MCP now offers page-defined WebMCP tools to agents by default.
  • Chrome DevTools MCP (Google, public preview September 2025, described as 1.0 at Google I/O 2026) lets coding agents record performance traces, read console and network data, inspect the DOM and accessibility tree, and automate flows in a live Chrome. Google's team found that a single broad "debug this page" tool worked poorly; decomposing it into about 25 specific tools, adding a slim mode and a CLI, and improving error messages so agents can recover on their own all mattered more than raw capability.
  • WebDriver BiDi continued to mature. Selenium's BiDi support now covers network interception, script, and log events, closing much of the gap that once pushed teams from Selenium to Playwright, and Puppeteer automates Firefox through BiDi.

WebMCP: websites declaring their own tools

WebMCP is a proposed web standard, drafted in the W3C Web Machine Learning Community Group with Google and Microsoft engineers, that lets a page expose structured tools to browser-based agents.

  • Two APIs. The imperative API registers JavaScript functions with a name, a description, a JSON input schema, and an execute handler. The declarative API annotates an ordinary HTML form with toolname and tooldescription attributes, and the browser derives the input schema from the form fields. A SubmitEvent.agentInvoked property lets a page tell whether an agent submitted the form.
  • Timeline. WebMCP appeared behind a flag in Chrome 146 Canary in early 2026. Google announced a public origin trial running from Chrome 149 through Chrome 156 at Google I/O in May 2026. A July 2026 community draft moved the entry point from navigator.modelContext to document.modelContext, and Chrome 150 deprecated the old location, so many early tutorials are already out of date.
  • Constraints. WebMCP works only in origin-isolated documents, is gated by a Permissions Policy, and is supported by Chrome DevTools for inspecting and testing registered tools. Other browser engines have not committed to it, and WebKit has reportedly objected.
  • Why it matters. In a demonstration at AI Engineer Europe 2026, a Chrome developer relations engineer showed an agent buying concert tickets in three tool calls (search, open page, purchase) instead of parsing the DOM, reading the accessibility tree, taking screenshots, and calculating click coordinates. She also recommended handing the final payment step back to the human. Google has claimed reliability and efficiency gains but had not, at the time of that talk, published numeric benchmarks.

Frameworks for building agents

  • browser-use (Python, open source, Y Combinator W25) runs a fully autonomous loop: the model sees page state, picks an action, and repeats until done. It is popular for prototyping and open-ended tasks; the trade-off is that it re-reasons on every run, so the same goal can take different paths on different days.
  • Stagehand (Browserbase, open source) takes a hybrid approach with act(), extract(), observe(), and an agent() mode, letting developers mix plain code with AI steps. Version 3 (October 29, 2025) was rewritten to talk to Chrome directly over CDP rather than through Playwright, and in January 2026 Browserbase extended it to Python, Go, Java, Ruby, Rust, and other languages.
  • Managed browser infrastructure (Browserbase, Steel, Anchor, and others) runs fleets of cloud browsers with session recording, persistent logins, and, increasingly, verified bot identity for agents.

How good are web agents, really?

  • "An Illusion of Progress?" Researchers at Ohio State University and UC Berkeley introduced Online-Mind2Web (300 realistic tasks on 136 live websites; accepted at COLM 2025). Several agents that reported close to 90 percent success on the older WebVoyager benchmark dropped sharply under careful human evaluation, and many recent agents did not outperform a simple baseline from early 2024. The team also built WebJudge, an automatic evaluator with about 85 percent agreement with human judges.
  • Leaderboards in 2026. On the HAL leaderboard snapshot for Online-Mind2Web from May 2026, the top configuration solved about 42 percent of tasks under automated judging. Reported scores vary widely depending on whether human judges, WebJudge, or a vendor's own grader was used, so compare only results that publish their judge and task-level traces.
  • WebArena (2023) found GPT-4 succeeded on 14.41 percent of tasks versus 78.24 percent for humans on its self-hosted sites. Agents have improved substantially since, but realistic task suites still leave a large gap.
  • Where time goes. The OSWorld-Human study (June 2025) measured a computer-use agent and found that planning and reflection consumed roughly 75 to 94 percent of elapsed task time. Faster clicking does not make an agent fast; fewer deliberation steps do. This supports the hybrid pattern of moving known steps into code.

Security research: prompt injection and the same-origin policy

  • Early disclosures (2025). Brave's security team disclosed indirect prompt injection against Comet in August 2025. Within about a day of Atlas's beta launch in October 2025, researchers demonstrated clipboard injection and other attacks, and LayerX disclosed "Tainted Memories" against Atlas.
  • Agentic Browsers and the Same-Origin Policy (2026). Franziska Roesner and David Kohlbrenner of the University of Washington tested seven agentic browsers and found that in four of them, a successful prompt injection could let a malicious site use the agent to read data from, or act on, other sites, effectively bypassing the same-origin policy. They demonstrated full cross-origin data theft against ChatGPT Atlas. Browsers that gave agents fewer permissions were generally safer, and Firefox's AI mode carried the lowest risk. The paper appeared at the ICLR 2026 Agents in the Wild workshop.
  • PleaseFix (March 2026). Zenity Labs disclosed a family of zero-click agent hijacks, first against Comet and later against several other agentic browsers, presented at Black Hat USA 2026.
  • BioShocking (June 2026). LayerX reported a technique that conditions an agent through a deceptive web "game" and successfully steered six AI browsers and extensions, from OpenAI, Anthropic, and Perplexity among others, into copying a user's SSH credentials from an authenticated GitHub repository to an attacker's page.
  • Attacks in the wild. Palo Alto Networks Unit 42 documented indirect prompt injection in production during 2026, with delivery methods including visible plain text, instructions hidden in HTML attributes, and text hidden with CSS.
  • Vendor defenses. Anthropic reports that on its current red-team evaluation, attacks that reached the model succeeded 17.6 percent of the time against Claude Opus 4.5 and 3.8 percent against Claude Opus 5 before additional safeguards, and that with its probes plus the action classifier, no attacks succeeded against Claude Sonnet 5, Opus 5, or Mythos 5, with 0.3 percent succeeding against Fable 5. These are vendor-reported results on vendor-built evaluations, and independent researchers continue to find new techniques.
  • Black Hat USA 2026. Brave engineer Artem Chaikin reported that every AI browser he analyzed was vulnerable to prompt injection and concluded that no known perfect defense exists. Proposed mitigations include alignment checks that compare each action with the user's original request, sanitizing content before the model sees it, dedicated injection-detection models, and task-scoped permissions.

The economics and law of automated access

  • Cloudflare's shift (July 1, 2025). Cloudflare began blocking AI crawlers by default for newly onboarded domains, launched a private beta of pay per crawl (sites can return HTTP 402 Payment Required with a price; crawlers that have registered and declare a willingness to pay receive the content), and adopted Web Bot Auth, where crawlers sign requests using HTTP Message Signatures so sites can verify who they are. Cloudflare later added a "signed agent" category for AI agents acting for users, and in September 2026 announced plans to separate search, training, and agent crawling more strictly and to evolve pay per crawl toward payment when content is actually used.
  • Amazon v. Perplexity. Amazon sued Perplexity in November 2025, alleging that Comet's shopping agent accessed customer accounts without Amazon's authorization and concealed that it was automated. A federal district judge granted a preliminary injunction in March 2026. In early August 2026, the Ninth Circuit vacated that injunction, finding on the current record that Amazon was unlikely to succeed on its federal anti-hacking claim because users, not Perplexity, were the ones accessing Amazon through the tool. The court stressed that its ruling was limited to the existing record and did not set broad rules for agentic AI; the case continues in district court.
  • Earlier foundations. Van Buren v. United States (2021) narrowed the U.S. Computer Fraud and Abuse Act; hiQ Labs v. LinkedIn addressed scraping of public profiles; and in 2024 a federal court ruled that Meta's terms did not bar Bright Data's logged-out scraping of public data. Robots.txt was formally standardized as RFC 9309 in 2022, but it remains a request, not an access control.

Browser platform changes that affect everyday automation

  • Manifest V3 and userscripts. Chrome finished phasing out Manifest V2 extensions around mid-2025. Userscript managers such as Tampermonkey now run on the chrome.userScripts API, which users must enable explicitly: before Chrome 138 through Developer Mode, and from Chrome 138 onward through a per-extension Allow User Scripts toggle on the extension's details page.
  • Trusted Types now ships in Chrome, Edge, Firefox (version 148), and Safari (version 26), so more sites can block the innerHTML-style shortcuts that many older bookmarklets rely on.
  • Chrome for Testing. Playwright switched from Chromium builds to Chrome for Testing builds in version 1.57 (late 2025), and dropped Manifest V2 extension support in version 1.55.

Office and VBA changes

  • Internet Explorer was retired as a desktop application in June 2022. VBA examples that automate InternetExplorer.Application are now legacy and unreliable.
  • VBScript deprecation. Microsoft plans to disable VBScript by default in Windows in approximately 2026 or 2027. VBA projects that call .vbs files or reference the "Microsoft VBScript Regular Expressions 5.5" library need updating. Starting with Microsoft 365 version 2508, RegExp classes are built into VBA itself.
  • Power Query in the browser. Microsoft rolled out the full Power Query editor to Excel for the web for business and enterprise subscribers in early 2026, so web-data queries can now be built and maintained without the desktop app.
  • Modern VBA browser automation. The open-source SeleniumVBA project drives Edge, Chrome, and Firefox from Office VBA, downloads matching WebDriver versions automatically, exposes Chrome DevTools Protocol commands, and has community add-ons for WebDriver BiDi. The older SeleniumBasic package has not been updated in years and breaks when browsers auto-update.

Choosing an Approach

Start at the simplest layer that works, and move up only when you must.

Situation Start with Why
Data is already available as an API, JSON, RSS, or CSV HTTP client (Requests, httpx, Power Query Web.Contents) Fastest, most stable, and lightest on the site
Static HTML page Requests plus Beautiful Soup, or Power Query From Web Simple parsing, easy to schedule
Content appears only after JavaScript runs Playwright, Selenium, or Power Query Web.BrowserContents A real browser must render the page
Large, multi-page crawl Scrapy (with scrapy-playwright when needed) Built-in throttling, retries, pipelines
One-off task on the page in front of you Bookmarklet or DevTools snippet No install; runs in your own session
Same tweak every time you visit a site Userscript (Tampermonkey, Violentmonkey) Runs automatically on matching URLs
Workflow centered on a spreadsheet Power Query, Office Scripts with Power Automate, or VBA Lives where the data ends up
Process spanning several business apps Power Automate Desktop, UiPath, n8n, Zapier Orchestration, connectors, approvals
Open-ended task on unfamiliar sites AI browser agent (Claude in Chrome, Gemini in Chrome, Comet, ChatGPT agent) Handles variation; supervise it
An AI coding agent needs to see and test a web app Playwright MCP or Chrome DevTools MCP Structured browser tools for any MCP client
Repeatable automation with occasional AI judgment Stagehand, or AI-written Playwright code Deterministic where possible, AI where needed
You own the site and want agents to use it well Semantic HTML, good accessibility, then WebMCP (experimental) Declare actions instead of making agents guess

A useful habit before writing any scraper: open the browser's DevTools, go to the Network tab, filter by Fetch/XHR, and reload the page. Many modern sites load their data as clean JSON from an internal endpoint. Requesting that JSON directly (where permitted) is usually simpler and far more robust than parsing rendered HTML.

Bookmarklets and In-Page JavaScript

YouTube search... ...Google search

What a Bookmarklet Is

A bookmarklet is a bookmark whose address begins with javascript: instead of https://. When you click it, the browser runs that JavaScript on the page you are currently viewing, with the same access to the page's content as the page's own scripts. Bookmarklets need no extension, no install, and no special permissions, which makes them one of the lowest-friction forms of web automation available.

Good uses for bookmarklets:

  • Exporting a table, list, or set of links from the current page.
  • Reformatting a page: removing clutter, enlarging text, highlighting keywords.
  • Copying the page title and URL in a specific format (Markdown, a citation, a ticket comment).
  • Filling a form you complete repeatedly with the same non-sensitive values.
  • Toggling editing mode on a page so you can annotate a screenshot.

Things bookmarklets cannot do well:

  • Run automatically (you must click them; use a userscript for that).
  • Run on browser-internal pages such as chrome:// settings or the extension store.
  • Reliably load outside code on sites with strict security policies (see below).
  • Keep state between pages, since each click starts fresh.

How to install one.

  • Desktop Chrome, Edge, Firefox, Safari: show the bookmarks bar, create a new bookmark, and paste the javascript: code into the URL field. Or drag a bookmarklet link from an install page onto the bar.
  • iPhone and iPad (Safari): bookmark any page, then edit the bookmark and replace its address with the bookmarklet code.
  • Android (Chrome): save the bookmark, then type the bookmark's name into the address bar and select it from the suggestions; tapping it from the bookmarks menu often does not run it.

Security policies and bookmarklets. The Content Security Policy specification says page policies should not interfere with bookmarklets, and current Chrome runs a bookmarklet's own code even on strict-CSP pages. Browser behavior has varied over the years, though, and anything your bookmarklet adds to the page is still subject to that page's rules. In practice:

  • A bookmarklet that injects <script src="https://other-site/..."> will be blocked on sites whose CSP does not allow that source. Self-contained bookmarklets avoid this problem.
  • On sites that enforce Trusted Types, assigning strings to innerHTML throws an error. Build elements with document.createElement and set textContent instead.
  • Setting styles through element.style in JavaScript generally works where injected <style> tags may be blocked.
  • fetch() calls to other domains are limited by the page's CSP connect-src and by CORS.

Asking a Chatbot for a Bookmarklet

Chatbots are very good at writing bookmarklets, because the programs are small, self-contained, and easy to test. A journalist writing in Fast Company in 2025 described using ChatGPT to build a library of personal bookmarklets without knowing JavaScript, and noted a common failure: chatbots sometimes add // comment lines, which break a bookmarklet once it is squeezed onto a single line.

A reliable workflow:

  1. Describe the goal and the page. Say what you want to happen and on which site. If selectors matter, paste a small, relevant chunk of the page's HTML (right-click, Inspect, then copy the element), with any personal data removed.
  2. State constraints up front using a prompt like the one below.
  3. Test in the console first. Open DevTools (F12), go to the Console tab, paste the readable version, and press Enter. Fix problems there; error messages are much clearer than a bookmark that silently does nothing.
  4. Read before you run. Ask the chatbot to explain each part. Confirm the code does not send data anywhere you did not expect.
  5. Convert and install. Paste the one-line javascript: version into a bookmark.
  6. Iterate. If it fails, paste the exact console error back to the chatbot.

A prompt that consistently produces good results:

Write a bookmarklet that [describe the goal, for example: downloads every
table on the current page as a separate CSV file].

Requirements:
- Plain modern JavaScript only. No external libraries and no network
  requests unless I ask for them.
- Wrap everything in an arrow-function IIFE: (() => { ... })();
- Do not use // comments. Use /* */ if a comment is truly needed.
- Do not use innerHTML. Build elements with createElement and textContent.
- Make it safe to click twice: a second click should cancel or toggle off.
- Show a short alert if nothing on the page matches.
- Give me (1) a readable, commented version to test in the DevTools
  console and (2) a single-line javascript: version for the bookmark.
- Explain in plain language what the code reads, changes, and sends.

Common problems and fixes:

  • Nothing happens. Open the console and look for errors. Check for // comments, "smart quotes" (curly quotation marks) introduced by a word processor or chat app, or a missing javascript: prefix.
  • Works on one site but not another. The second site probably has a stricter CSP or Trusted Types, or uses different markup. Ask the chatbot to avoid external scripts, innerHTML, and eval.
  • Clicks are ignored. Some pages ignore script-generated (untrusted) clicks. A bookmarklet cannot produce trusted input; use Playwright or a browser agent for that step.
  • Characters break the bookmark. Characters like % and # can be misread in a URL. Encode the code with encodeURIComponent, as the build script in the next section does.

Example Bookmarklets

Each line below is a complete bookmarklet. Paste it into a bookmark's URL field.

Copy the page title and URL as a Markdown link (works on HTTPS pages):
javascript:(()=>{const t=`[${document.title}](${location.href})`;navigator.clipboard.writeText(t).then(()=>alert('Copied: '+t),()=>prompt('Copy this:',t))})();

Count the words in the current text selection:
javascript:(()=>{const s=String(getSelection()).trim();alert(s?s.split(/\s+/).length+' words':'Select some text first.')})();

Outline every link that leaves the current site:
javascript:(()=>{let n=0;document.querySelectorAll('a[href]').forEach(a=>{if(a.host&&a.host!==location.host){a.style.outline='2px solid #e03131';n++}});alert(n+' external links highlighted')})();

Toggle page editing so you can annotate before a screenshot:
javascript:(()=>{document.designMode=document.designMode==='on'?'off':'on';alert('Editing '+document.designMode)})();

List every form field name and type in a new window (useful when writing automation):
javascript:(()=>{const rows=[...document.querySelectorAll('input,select,textarea')].map(e=>[e.tagName.toLowerCase(),e.type||'',e.name||'',e.id||''].join('\t'));const w=open();w.document.body.style.whiteSpace='pre';w.document.body.textContent='tag\ttype\tname\tid\n'+rows.join('\n')})();

Userscripts and Extensions

When you want code to run automatically every time you visit certain pages, use a userscript instead of a bookmarklet. Userscripts are JavaScript files with a metadata header that tells the manager (Tampermonkey, Violentmonkey, ScriptCat, or the Userscripts app on Safari) which URLs to run on.

// ==UserScript==
// @name         Sticky table headers for reports
// @match        https://reports.example.com/*
// @grant        none
// ==/UserScript==
(() => {
  for (const th of document.querySelectorAll('table thead th')) {
    th.style.position = 'sticky';
    th.style.top = '0';
    th.style.background = 'white';
    th.style.zIndex = '1';
  }
})();

Since Chrome's move to Manifest V3, userscript managers depend on the chrome.userScripts API. In Chrome 138 and later, you must open the manager's details page in chrome://extensions and switch on Allow User Scripts; earlier versions required Developer Mode. Firefox users are not affected by this change.

For automation that needs background processing, cross-site requests, or a toolbar interface, write a small Manifest V3 extension. Chatbots can scaffold these too, but extensions request broad permissions, so review them carefully and load them unpacked rather than publishing anything you have not read.

DevTools Snippets and the Console

For code you run occasionally but want to keep, Chrome and Edge DevTools offer Snippets (Sources panel, Snippets tab): saved scripts you can run on any page with Ctrl+Enter. Firefox's console has a multi-line editor mode. Snippets are also the best place to develop a bookmarklet before converting it.

Complete Build and Deploy Example

This example builds two real automations from source to production:

  • Part 1 builds a "Table to CSV" bookmarklet from readable source, tests it automatically with Playwright, and publishes an install page on GitHub Pages every time you push a change.
  • Part 2 builds a scheduled Python and Playwright scraper that runs daily on GitHub Actions, checks robots.txt, rate-limits itself, validates its output, and commits the data to the repository.

Both run entirely on free GitHub features for public repositories. Both target pages designed for practice or your own content; adapt them only to sites whose terms and robots.txt permit it.

You can build either part with an AI coding assistant. A good approach: create the empty repository, paste the file list below into the assistant, ask it to explain each file, then run the build and tests yourself.

Part 1: Table to CSV Bookmarklet with Tests and GitHub Pages

Repository layout

table-to-csv/
  package.json
  build.mjs
  src/table-to-csv.js
  test/fixture.html
  tests/bookmarklet.spec.js
  .github/workflows/pages.yml

Step 1. The readable source (src/table-to-csv.js). This version is what you edit and review. The build step strips comments and compresses it.

/*
  Table to CSV bookmarklet.
  Click the bookmark. If the page has one table, it downloads immediately.
  If it has several, each gets an orange outline; click the one you want.
  Press Esc, or click the bookmark again, to cancel.
*/
(() => {
  if (window.__tableToCsv) {
    window.__tableToCsv.stop();
    return;
  }

  const tables = Array.from(document.querySelectorAll('table'))
    .filter((t) => t.rows.length > 0);

  if (tables.length === 0) {
    alert('Table to CSV: no tables found on this page.');
    return;
  }

  const clean = (cell) => cell.innerText.replace(/\s+/g, ' ').trim();

  const field = (value) =>
    /[",\r\n]/.test(value) ? '"' + value.replace(/"/g, '""') + '"' : value;

  const toCsv = (table) =>
    Array.from(table.rows)
      .map((row) => Array.from(row.cells).map((c) => field(clean(c))).join(','))
      .join('\r\n');

  const download = (text) => {
    const day = new Date().toISOString().slice(0, 10);
    const host = location.hostname.replace(/[^a-z0-9.-]/gi, '_') || 'page';
    /* The byte order mark helps Excel open UTF-8 text correctly. */
    const blob = new Blob(['\ufeff' + text], { type: 'text/csv;charset=utf-8' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = host + '-table-' + day + '.csv';
    document.body.appendChild(link);
    link.click();
    link.remove();
    setTimeout(() => URL.revokeObjectURL(url), 2000);
  };

  if (tables.length === 1) {
    download(toCsv(tables[0]));
    return;
  }

  const before = new Map(tables.map((t) => [t, t.style.outline]));
  tables.forEach((t) => { t.style.outline = '3px dashed #e8590c'; });

  const onClick = (event) => {
    const table = event.target.closest('table');
    if (!table || !before.has(table)) return;
    event.preventDefault();
    event.stopPropagation();
    download(toCsv(table));
    stop();
  };

  const onKey = (event) => {
    if (event.key === 'Escape') stop();
  };

  function stop() {
    tables.forEach((t) => { t.style.outline = before.get(t); });
    document.removeEventListener('click', onClick, true);
    document.removeEventListener('keydown', onKey, true);
    delete window.__tableToCsv;
  }

  document.addEventListener('click', onClick, true);
  document.addEventListener('keydown', onKey, true);
  window.__tableToCsv = { stop };
})();

Design notes: it uses no external scripts, no innerHTML, and no network requests, so it works on most sites with strict security policies; styles are set through element.style; it is safe to click twice; and it quotes CSV fields correctly, including values with commas, quotes, or line breaks.

Step 2. The build script (build.mjs). It minifies the source with Terser, percent-encodes it, and writes both the raw bookmarklet and an install page.

import { readFile, writeFile, mkdir } from 'node:fs/promises';
import { minify } from 'terser';

const pkg = JSON.parse(await readFile('package.json', 'utf8'));
const source = await readFile('src/table-to-csv.js', 'utf8');

const { code } = await minify(source, {
  ecma: 2020,
  compress: { passes: 2 },
  mangle: true,
  format: { comments: false, ascii_only: true },
});

const href = 'javascript:' + encodeURIComponent(code);
const attr = (s) => s.replace(/&/g, '&').replace(/"/g, '"');
const text = (s) => s.replace(/&/g, '&').replace(/</g, '<');

const html = `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Table to CSV bookmarklet</title>
<style>
  body { font: 16px/1.5 system-ui, sans-serif; max-width: 42rem; margin: 2rem auto; padding: 0 1rem; }
  a.bookmarklet { display: inline-block; padding: .6rem 1rem; border-radius: .5rem;
    background: #e8590c; color: #fff; text-decoration: none; font-weight: 600; }
  textarea { width: 100%; font: 12px monospace; }
</style>
</head>
<body>
<h1>Table to CSV</h1>
<p>Drag this button to your bookmarks bar:</p>
<p><a class="bookmarklet" href="${attr(href)}">Table to CSV</a></p>
<ol>
  <li>Open any page that contains an HTML table.</li>
  <li>Click the bookmark. If there are several tables, each gets an orange outline.</li>
  <li>Click the table you want. A CSV file downloads. Press Esc to cancel.</li>
</ol>
<details>
  <summary>Phone or tablet? Copy the code instead.</summary>
  <p>Create any bookmark, edit it, and replace its address with this text:</p>
  <textarea readonly rows="6">${text(href)}</textarea>
</details>
<p>Version ${pkg.version}. Runs entirely in your browser and sends no data anywhere.</p>
</body>
</html>
`;

await mkdir('dist', { recursive: true });
await writeFile('dist/index.html', html);
await writeFile('dist/bookmarklet.txt', href + '\n');
console.log(`Built v${pkg.version}: ${href.length} characters`);

Step 3. The package file (package.json).

{
  "name": "table-to-csv-bookmarklet",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "build": "node build.mjs",
    "test": "playwright test"
  },
  "devDependencies": {
    "@playwright/test": "^1.56.0",
    "terser": "^5.31.0"
  }
}

Step 4. A test page and an automated test. The fixture (test/fixture.html) contains two tables, so the test exercises the "pick a table" path:

<!doctype html>
<meta charset="utf-8">
<title>Bookmarklet fixture</title>
<table id="people">
  <tr><th>Name</th><th>Role</th></tr>
  <tr><td>Ada</td><td>Engineer</td></tr>
</table>
<table id="prices">
  <tr><th>Item</th><th>Price</th></tr>
  <tr><td>Widget</td><td>1,200</td></tr>
</table>

The test (tests/bookmarklet.spec.js) runs the built bookmarklet exactly as a browser would, clicks the second table, and checks the downloaded CSV:

import { test, expect } from '@playwright/test';
import { readFile } from 'node:fs/promises';
import path from 'node:path';

test('downloads the clicked table as CSV', async ({ page }) => {
  const href = (await readFile('dist/bookmarklet.txt', 'utf8')).trim();
  const code = decodeURIComponent(href.slice('javascript:'.length));

  await page.goto('file://' + path.resolve('test/fixture.html'));
  await page.evaluate(code);

  const downloadPromise = page.waitForEvent('download');
  await page.locator('#prices').click();
  const download = await downloadPromise;

  const csv = await readFile(await download.path(), 'utf8');
  expect(csv).toContain('Item,Price');
  expect(csv).toContain('Widget,"1,200"');
});

Step 5. Build and test locally.

npm install                              # creates package-lock.json; commit it
npx playwright install chromium
npm run build
npm test

Open dist/index.html in your browser, drag the button to your bookmarks bar, and try it on a page with a table.

Step 6. Deploy automatically to GitHub Pages (.github/workflows/pages.yml). In the repository's Settings, open Pages and set the source to GitHub Actions. Every push to main will then build, test, and publish. If the test fails, nothing is deployed.

name: Build, test, and deploy bookmarklet

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: pages
  cancel-in-progress: true

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npm run build
      - run: npx playwright install --with-deps chromium
      - run: npm test
      - uses: actions/configure-pages@v5
      - uses: actions/upload-pages-artifact@v3
        with:
          path: dist

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - id: deployment
        uses: actions/deploy-pages@v4

After the first successful run, your install page is live at https://YOUR-USERNAME.github.io/table-to-csv/. To release a new version, edit the source, bump version in package.json, and push. Users must re-drag the button to get updates, because a bookmarklet stores its code inside the bookmark. GitHub periodically releases new major versions of these actions, so check for newer versions when you set this up.

Part 2: A Scheduled Python Scraper on GitHub Actions

This scraper collects quotes from quotes.toscrape.com/js/, a practice site whose content is rendered by JavaScript, so a plain HTTP request would return almost nothing. It uses Playwright for Python.

Repository layout

quote-tracker/
  requirements.txt
  scrape.py
  data/                    (created on first run)
  .github/workflows/scrape.yml

requirements.txt:

playwright>=1.55,<2

scrape.py:

"""Scrape quotes from a JavaScript-rendered practice site and save a dated CSV."""
import csv
import datetime as dt
import pathlib
import time
import urllib.robotparser

from playwright.sync_api import sync_playwright

BASE = "https://quotes.toscrape.com"
START = f"{BASE}/js/"
USER_AGENT = "QuoteTrackerDemo/1.0 (+https://github.com/YOUR-USERNAME/quote-tracker)"
DELAY_SECONDS = 1.0
OUT = pathlib.Path("data") / f"quotes-{dt.date.today().isoformat()}.csv"


def allowed(url: str) -> bool:
    """Respect robots.txt. If it cannot be read at all, stop rather than guess."""
    parser = urllib.robotparser.RobotFileParser(f"{BASE}/robots.txt")
    try:
        parser.read()
    except OSError:
        return False
    return parser.can_fetch(USER_AGENT, url)


def scrape() -> list[dict]:
    rows = []
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page(user_agent=USER_AGENT)
        url = START
        while url:
            page.goto(url, wait_until="domcontentloaded")
            page.locator("div.quote").first.wait_for(timeout=15000)
            for quote in page.locator("div.quote").all():
                rows.append({
                    "text": quote.locator("span.text").inner_text(),
                    "author": quote.locator("small.author").inner_text(),
                    "tags": ";".join(quote.locator("a.tag").all_inner_texts()),
                })
            next_link = page.locator("li.next a")
            url = BASE + next_link.get_attribute("href") if next_link.count() else None
            time.sleep(DELAY_SECONDS)
        browser.close()
    return rows


def main() -> None:
    if not allowed(START):
        raise SystemExit("robots.txt does not allow this crawl; stopping.")
    rows = scrape()
    if len(rows) < 10:
        raise SystemExit(f"Only {len(rows)} rows scraped; the page may have changed.")
    OUT.parent.mkdir(exist_ok=True)
    with OUT.open("w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=["text", "author", "tags"])
        writer.writeheader()
        writer.writerows(rows)
    print(f"Saved {len(rows)} quotes to {OUT}")


if __name__ == "__main__":
    main()

Run it locally first:

python -m venv .venv
source .venv/bin/activate        # on Windows: .venv\Scripts\activate
pip install -r requirements.txt
python -m playwright install chromium
python scrape.py

.github/workflows/scrape.yml runs it daily and commits new data:

name: Daily scrape

on:
  schedule:
    - cron: "17 6 * * *"     # 06:17 UTC every day; avoid the busy top of the hour
  workflow_dispatch:

permissions:
  contents: write

jobs:
  scrape:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: pip
      - run: pip install -r requirements.txt
      - run: python -m playwright install --with-deps chromium
      - run: python scrape.py
      - name: Commit new data
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add data/
          git diff --cached --quiet || git commit -m "Daily scrape $(date -u +%F)"
          git push

Production notes:

  • Fail loudly. The script exits with an error if robots.txt forbids the crawl or if it collects suspiciously few rows. GitHub then marks the run as failed and notifies you, which is how you find out the site changed.
  • Schedules are approximate. GitHub may delay scheduled runs during busy periods, and it disables schedules in public repositories after 60 days without repository activity.
  • Secrets. If a scraper needs to log in, store credentials in the repository's encrypted Secrets, never in code, and prefer a dedicated low-privilege account. Better still, check whether the site offers an API with tokens.
  • Data you collect. Avoid collecting personal data unless you have a lawful basis and a real need.
  • Agent-assisted maintenance. When the site changes and the run fails, an AI coding assistant connected to Playwright MCP can open the page, inspect the new structure, and propose updated selectors. You review the change and commit it; the scheduled job stays deterministic.

AI Browser Agents and MCP in Practice

Using Consumer Browser Agents Safely

Claude in Chrome, Gemini in Chrome, Comet, ChatGPT agent, and Copilot in Edge can complete multi-step tasks in your browser. Practical guidance drawn from vendor documentation and 2025 to 2026 security research:

  • Use a separate browser profile for agent work, signed in only to the accounts the task needs. Agents inherit whatever sessions the profile holds.
  • Keep sensitive sites out of scope. Banking, password managers, health portals, and admin consoles are the highest-value targets for injected instructions. Use site allowlists where the product offers them.
  • Keep consequential steps manual. Approve purchases, sends, deletions, and permission changes yourself, even when auto-approval is available.
  • Watch for odd behavior. An agent that suddenly opens your email or a site you did not mention while "summarizing a page" may be following injected instructions. Stop it.
  • Prefer read-only tasks (research, comparison, extraction) when trying a new agent.

Giving Coding Agents a Browser with MCP

Most MCP clients (Claude Code, Claude Desktop, Cursor, VS Code, Gemini CLI) accept a configuration like this:

{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp@latest", "--isolated"]
    },
    "chrome-devtools": {
      "command": "npx",
      "args": ["chrome-devtools-mcp@latest"]
    }
  }
}

Since Playwright 1.62 you can also run the bundled server with npx playwright mcp. With either server connected, you can ask an agent to "open the staging site, log in with the test account from the fixtures file, add an item to the cart, and tell me if any console errors appear," then ask it to write the steps as a Playwright test you can keep.

Match browser access to the environment, following the tiers the Chrome DevTools team recommends:

  • Local development: a human is present and grants time-limited access to a browser profile. Chrome DevTools MCP deliberately asks for consent on each connection to a live browser.
  • Continuous integration: use containers and throwaway browser profiles with only test data.
  • Open internet browsing: keep CI-level isolation and add domain allowlists and prompt-injection mitigations, because any page can attempt to instruct the agent.

Neither Playwright MCP nor Chrome DevTools MCP is a security boundary on its own. Their documentation says so, and origin allowlists do not prevent redirects.

Making Your Own Site Agent-Ready with WebMCP

If you run a website, the first steps help every visitor: semantic HTML, correct labels and roles, fast pages, and clear user flows. Agents that read accessibility trees work far better on accessible sites. WebMCP then lets you declare actions directly.

Imperative API (experimental; feature-detect, because the API is in an origin trial and has already moved once):

const mc = document.modelContext ?? navigator.modelContext;

if (mc) {
  mc.registerTool({
    name: 'search_catalog',
    description: 'Search the product catalog by keyword. Use this when the user asks to find or compare products.',
    inputSchema: {
      type: 'object',
      properties: {
        query: { type: 'string', description: 'Words to search for' }
      },
      required: ['query'],
      additionalProperties: false
    },
    execute: async ({ query }) => {
      const res = await fetch('/api/search?q=' + encodeURIComponent(query));
      const items = await res.json();
      showResults(items);   /* keep the visible page in sync with what the agent did */
      return { content: [{ type: 'text', text: JSON.stringify(items.slice(0, 10)) }] };
    }
  });
}

Declarative API on an existing form:

<form action="/subscribe" method="post"
      toolname="subscribe_newsletter"
      tooldescription="Subscribe an email address to the weekly newsletter">
  <label>Email <input name="email" type="email" required></label>
  <button>Subscribe</button>
</form>

Test locally by enabling the WebMCP testing flag in a recent Chrome, or register for the origin trial to test with real visitors. Chrome DevTools can inspect and invoke registered tools, Google's Model Context Tool Inspector extension can exercise them with a model, and Playwright 1.64's page.webmcp.tools() and page.webmcp.callTool() let you write automated tests for them. Keep payment and other irreversible steps as visible, human-confirmed actions.

Browser Automation Frameworks

Tool Origin How it controls the browser Languages Best for
Selenium 4 ThoughtWorks, 2004; W3C WebDriver standard WebDriver Classic and WebDriver BiDi Java, Python, C#, JavaScript, Ruby, and others Cross-browser testing, Selenium Grid, large existing suites
Playwright Microsoft, 2020 CDP for Chromium; Playwright's own protocols for Firefox and WebKit JavaScript and TypeScript, Python, Java, .NET Modern testing, scraping, codegen, AI agents through MCP
Puppeteer Google Chrome team, 2017 CDP; WebDriver BiDi for Firefox JavaScript and TypeScript Chrome automation, PDFs, screenshots
Chrome DevTools MCP Google, 2025 CDP (through Puppeteer) Any MCP client Debugging and performance work by coding agents
Stagehand Browserbase, 2024 CDP (version 3 and later) TypeScript, Python, and others Hybrid code plus AI steps with caching
browser-use Open source, 2024 Playwright and CDP Python Autonomous agent loops and prototyping
SeleniumVBA Open-source community WebDriver, with CDP and BiDi add-ons Office VBA Browser automation from Excel and Access

Practical tips that apply across frameworks:

  • Prefer user-facing locators (role, label, visible text, test IDs) over brittle CSS paths. Playwright's getByRole and getByLabel survive redesigns far better than div:nth-child(3) > span.
  • Let the framework wait. Use auto-waiting and condition-based assertions rather than fixed sleeps.
  • Record, then edit. npx playwright codegen https://example.com records your clicks as code. Selenium IDE does the same in a browser extension.
  • Reuse logins. Save authenticated storage state once and load it in later runs, instead of automating the login form every time.
  • Use traces. Playwright's Trace Viewer shows every action, network request, and DOM snapshot of a failed run.

A minimal Selenium 4 example (Selenium Manager finds or downloads a matching driver automatically, so the old "download chromedriver.exe" step is gone):

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.implicitly_wait(5)
driver.get("https://quotes.toscrape.com/js/")
for quote in driver.find_elements(By.CSS_SELECTOR, "span.text")[:3]:
    print(quote.text)
driver.quit()

Beautiful Soup and Requests

Requests and Beautiful Soup remain the quickest way to pull data from static pages. The pattern: request the page with a timeout and an honest User-Agent, check the status, parse the HTML, and select elements with CSS selectors.

import requests
from bs4 import BeautifulSoup

session = requests.Session()
session.headers["User-Agent"] = "MyResearchBot/1.0 (contact: you@example.org)"

response = session.get("https://books.toscrape.com/", timeout=20)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")

for book in soup.select("article.product_pod"):
    title = book.h3.a["title"]
    price = book.select_one("p.price_color").get_text(strip=True)
    print(title, price)

When to reach for something else:

  • httpx adds async requests and HTTP/2 when you need many concurrent requests.
  • lxml or selectolax parse much faster than the default parser on large volumes.
  • Scrapy handles crawling at scale with throttling, retries, caching, and export pipelines; scrapy-playwright renders JavaScript pages inside Scrapy.
  • Playwright for Python handles pages that need a real browser, as in the deploy example above.
  • Regular expressions are useful for cleaning extracted text (prices, dates, IDs), but are a poor tool for parsing HTML structure.
  • LLM-assisted extraction (sending cleaned page text to a model with a JSON schema) can pull structured fields from messy, inconsistent pages. Validate the output, because models can invent values that are not on the page.


Jupyter Notebook

YouTube search... ...Google search

Notebooks are ideal for exploring a site, testing selectors, and analyzing what you collect. Points that trip people up in 2026:

  • Use Playwright's async API in notebooks. Jupyter and Colab already run an event loop, so the synchronous API raises an error. IPython supports top-level await, which makes the async API straightforward.
  • Install browsers in the notebook environment. On Colab, run the install commands in a cell each session, since the virtual machine is reset.
  • Expect more blocking from cloud notebooks. Colab and other hosted notebooks run on shared datacenter IP addresses, which many sites challenge or rate-limit more aggressively than home connections.
  • Notebook AI assistants can now write and debug scraping cells, but the same rules apply: read the code, respect the site's terms, and keep credentials out of shared notebooks.
!pip -q install playwright
!playwright install --with-deps chromium

from playwright.async_api import async_playwright

async with async_playwright() as p:
    browser = await p.chromium.launch()
    page = await browser.new_page()
    await page.goto("https://quotes.toscrape.com/js/")
    quotes = await page.locator("span.text").all_inner_texts()
    await browser.close()

quotes[:3]

Excel

YouTube search... ...Google search

Modern Excel Options

Before writing VBA, check whether a no-code or low-code option already does the job.

  • Power Query (Data, From Web). Imports HTML tables, JSON, CSV, and XML from a URL, then lets you clean and reshape the data with refreshable steps. Web.Contents is the right function for APIs and files. The older Web.Page function depends on legacy Internet Explorer components. Web.BrowserContents renders JavaScript-heavy pages using the Microsoft Edge WebView2 runtime and can wait for a specific element to appear; it is fully supported in Power BI Desktop, while support in Excel varies by version and product. Since early 2026, business and enterprise subscribers can build and edit Power Query queries directly in Excel for the web.
  • Office Scripts with Power Automate. Office Scripts (TypeScript) automate Excel for the web, and Power Automate can run them on a schedule or when an event occurs, entirely in the cloud. This is the modern replacement for many "open the workbook every morning and run the macro" routines.
  • Power Automate Desktop. Included with Windows, it records and replays browser and desktop actions (robotic process automation) and can write results into Excel. It suits portals that require clicking through several screens.
  • Python in Excel. Useful for analysis, but its Python runs in a Microsoft cloud sandbox without general internet access, so bring web data in through Power Query first.
  • Google Sheets. IMPORTHTML, IMPORTXML, and IMPORTDATA pull tables, XPath matches, and CSV files into a sheet with a single formula.

Note on older material below. Many classic Excel scraping tutorials automate Internet Explorer through InternetExplorer.Application. Microsoft retired the Internet Explorer desktop application in June 2022, so these techniques are legacy: they may fail outright or behave inconsistently. The concepts (navigating, reading tables, filling forms, GET and POST requests) still transfer directly to the modern tools described here.

VBA using Selenium

Most older tutorials use SeleniumBasic, which has not been updated in years. Its bundled drivers fall out of step whenever Chrome or Edge updates itself, producing errors such as "unexpected command response." SeleniumVBA solves this by checking and downloading a matching driver on each run, and it adds features such as PDF capture, CDP access, and (through community add-ons) WebDriver BiDi event handling.

VBA

There are two ways to get information from websites programmatically: by downloading their data, or by parsing their HTML. WiseOwl

For the "downloading their data" approach, VBA can call web APIs directly without any browser. This pattern still works in current Office versions:

Sub GetRepositoryInfo()
    Dim http As Object
    Set http = CreateObject("MSXML2.XMLHTTP.6.0")
    http.Open "GET", "https://api.github.com/repos/microsoft/playwright", False
    http.setRequestHeader "User-Agent", "ExcelVBA"
    http.send

    If http.Status = 200 Then
        ThisWorkbook.Worksheets(1).Range("A1").Value = http.responseText
    Else
        MsgBox "Request failed with HTTP status " & http.Status
    End If
End Sub

Parse the returned JSON with a library such as VBA-JSON, or let Power Query handle it. If your macros use VBScript regular expressions, plan for Microsoft's VBScript deprecation: from Microsoft 365 version 2508, RegExp is built into VBA, so the external "Microsoft VBScript Regular Expressions 5.5" reference is no longer needed. Chatbots are effective at translating old Internet Explorer macros into XMLHTTP, Power Query, or SeleniumVBA equivalents; paste the macro and ask for a line-by-line explanation of the changes.

Internet Explorer Automation Examples (Legacy)

The following resources demonstrate form filling and scraping by automating Internet Explorer from VBA. They remain useful for understanding the logic of browser automation, but the technique itself depends on the retired Internet Explorer application. Practice sites from that era may also no longer be online.


The Physics

Software has no forces or fields, but web automation does obey hard technical laws. Understanding them explains why automations fail and how to make them sturdy.

1. Requests, latency, and throughput. Every page load is one or more HTTP request and response round trips. For a scraper, a useful approximation is:

throughput (requests per second) = concurrency / average latency

Four parallel workers with 0.8 seconds of latency each produce about 5 requests per second. That is fast for you and potentially heavy for a small site. Polite crawlers cap concurrency per domain and add a delay d between requests, which bounds each worker to at most 1/d requests per second.

2. Retries and backoff. When a server returns HTTP 429 (Too Many Requests) or 503, the correct response is to slow down, not to hammer harder. Exponential backoff with jitter is the standard approach:

wait = min(cap, base * 2^attempt) + random_jitter

With a 1-second base, waits grow 1, 2, 4, 8, 16 seconds. The jitter keeps many clients from retrying in lockstep. Always honor a Retry-After header when a server sends one.

3. Rendering, the event loop, and waiting. A browser parses HTML into the DOM, runs JavaScript on a single main thread, fetches more data, and re-renders. Many modern pages first render static HTML and then hydrate it, attaching JavaScript behavior afterward. A button can look clickable before its event handler exists, so a click that arrives too early silently does nothing. This is why fixed sleep() calls are fragile and why Playwright's auto-waiting checks that a target is unique, visible, stable across animation frames, enabled, and able to receive pointer events before acting. Wait for the condition the next step actually needs, not for an arbitrary amount of time.

4. Compounding reliability. Multi-step automation multiplies small failure rates. If each step succeeds independently with probability p, a task of n steps succeeds with probability:

P(task) = p^n

At 99 percent per step, a 30-step task succeeds about 74 percent of the time. At 98 percent, it drops to about 55 percent. At 99.5 percent, it rises to about 86 percent. This one relationship explains why long agent tasks are hard, why verification after each step matters, and why converting known steps into deterministic code (where p approaches 1) has such a large effect.

5. Observation cost for agents. Language models pay for every token they read. A raw DOM can run to hundreds of kilobytes; an accessibility snapshot of the same page is usually far smaller; a screenshot has a roughly fixed cost but loses exact values. Google's Chrome DevTools team has described an early experiment in which a single performance trace (multiple megabytes, roughly 50,000 lines of JSON) overwhelmed agent context windows, so their tools now return concise semantic summaries instead. The design rule: give the model the smallest representation that still contains the information needed for the next decision, and measure tokens per successful outcome rather than tokens alone.

6. Coordinates and stale targets. Vision agents choose points on screenshots, but browsers accept input in CSS pixels. For an unrotated screenshot crop with origin (ox, oy) and a scale of k image pixels per CSS pixel:

x_input = o_x + x_image / k, y_input = o_y + y_image / k

For example, on a high-density display with k = 2 and a crop starting at (0, 120), the image point (600, 400) maps to the page point (300, 320). Even a correct conversion goes stale if an advertisement loads and pushes the layout down, which is why agents must re-observe after layout changes and why structured targets (element references, roles, or declared tools) beat raw coordinates.

7. Event trust. Browsers stamp every event with isTrusted. Events created by page scripts, including element.click() and dispatchEvent(), are untrusted. Events that come from real input devices, or from the browser's own input pipeline as used by WebDriver and the DevTools Protocol's Input domain, are trusted. Some pages ignore untrusted clicks entirely. This explains why a bookmarklet's synthetic click sometimes does nothing while the same click from Playwright works. It is also why sites rely on many other signals besides isTrusted to judge whether a session is automated.

8. Origins and security boundaries. The same-origin policy (in place since the mid-1990s) prevents a script on one site from reading another site's data. Content Security Policy (CSP) lets a site restrict which scripts may run and where the page may connect. Trusted Types lets a site forbid assigning raw strings to dangerous sinks like innerHTML; on pages that enforce it, such assignments throw a TypeError. These rules shape what bookmarklets and userscripts can do. They also matter for AI agents: an agent that reads one tab and acts in another can carry information across origins, which is exactly the boundary the same-origin policy was designed to enforce.

9. Control protocols. Automation tools talk to browsers through a few protocols:

  • WebDriver Classic (W3C): command and response over HTTP; universal and stable, but historically one-directional.
  • WebDriver BiDi (W3C): a bidirectional WebSocket protocol with events for network activity, console logs, and script execution; now supported by Selenium and Puppeteer across Chromium and Firefox.
  • Chrome DevTools Protocol (CDP): the protocol behind Chrome's F12 DevTools; extremely capable but Chromium-specific and frequently changing.
  • Playwright's browser protocols: Playwright uses CDP for Chromium and its own patched-browser protocols for Firefox and WebKit.

10. Identity on the network. Historically a crawler's identity was just a claim in its User-Agent header, which anyone can fake. Newer schemes such as Web Bot Auth sign each request with a key using HTTP Message Signatures (RFC 9421), so a site can verify which operator sent it. This shift from claimed to proven identity is reshaping who gets reliable access to the web, as described in Recent Developments.


Legal, Ethical, and Security Considerations

This section is general information, not legal advice. Laws differ by country, and outcomes depend on specific facts.

Before You Automate a Site

  • Read the terms of service. Many sites prohibit automated access or scraping. Violating terms can lead to account bans or contract claims, even where criminal law does not apply.
  • Check robots.txt. It expresses the site owner's wishes about crawling (RFC 9309). It does not technically block anyone, but ignoring it is a signal of bad faith and many sites now enforce their wishes at the network level.
  • Prefer official APIs, feeds, and data downloads. They are more reliable and clearly permitted.
  • Identify yourself. Use an honest User-Agent with contact information. Do not impersonate other bots or browsers to get around restrictions.
  • Be gentle. Limit concurrency, add delays, cache responses, and back off on errors. A scraper that degrades a small site's performance causes real harm.
  • Do not bypass access controls. Logging in with credentials you were not given, defeating CAPTCHAs on others' services, or circumventing technical blocks raises serious legal risk in many jurisdictions.
  • Handle personal data carefully. Privacy laws such as the GDPR apply to personal data even when it is publicly visible. Collect only what you need and secure it.
  • Respect copyright. Being able to fetch content does not grant the right to republish it or, in some jurisdictions, to use it for AI training where rights holders have opted out.

The Legal Landscape in Brief

  • United States. Van Buren v. United States (2021) narrowed the Computer Fraud and Abuse Act's "exceeds authorized access" clause. hiQ Labs v. LinkedIn addressed scraping of public profile data, and the case ultimately ended on contract grounds. In 2024, a federal court found that Meta's terms did not prohibit Bright Data's logged-out scraping of public data. In Amazon v. Perplexity, the Ninth Circuit in August 2026 vacated a preliminary injunction against Comet's shopping agent, reasoning on the existing record that users, not Perplexity, accessed Amazon; the case continues, and the court declined to set general rules for AI agents.
  • Infrastructure as policy. Much of the practical "law" of automated access is now set by infrastructure providers. Cloudflare blocks AI crawlers by default for new domains, offers pay-per-crawl through HTTP 402 responses, and verifies bots through signed requests. Sites protected by these services can distinguish verified, declared agents from unverified ones.

Security for People Who Automate

  • Treat bookmarklets and userscripts as software you are installing. They run with full access to every page you use them on, including your logged-in sessions. Avoid "loader" bookmarklets that fetch and execute code from a remote server you do not control, since whoever controls that server controls the code you run.
  • Keep secrets out of scripts. Use environment variables, CI secrets, or a password manager. Never paste passwords or API keys into a chatbot along with your code.
  • Use least privilege. Give automation dedicated accounts with only the permissions it needs.
  • Assume AI agents can be manipulated. Any page, email, document, or comment an agent reads can contain instructions. Separate browsing profiles, domain allowlists, human approval for consequential actions, and monitoring are the current best defenses; none is perfect.
  • Log what automation does. Keep records of actions and their outcomes, especially for anything that writes, buys, sends, or deletes, so mistakes can be traced and reversed.

Further Resources

References

Frameworks and protocols

WebMCP

Agentic browsers and security research

Evaluation

Access, identity, and law

Bookmarklets, userscripts, and browser security

Excel and VBA

Additional Viewing

The agent-ready web: Simplify user actions with WebMCP (AI Engineer Europe 2026, about 22 minutes). Tara Agyemang of Google's Chrome developer relations team explains why agents struggle with DOM parsing and screenshots, then demonstrates WebMCP's declarative and imperative APIs, including an agent buying concert tickets in three tool calls. It is the clearest introduction to how site-declared tools work and why the final payment step should stay with the user.

Building Agent Interfaces: Lessons from Chrome DevTools (MCP) for Agents (AI Engineer Europe 2026, about 23 minutes). Michael Hablich, product manager for Chrome Developer Tools, shares four engineering lessons from building Chrome DevTools MCP: measuring tokens per successful outcome, designing recoverable errors, writing minimal but clear tool descriptions, and matching browser access to trust boundaries. Essential viewing for anyone connecting coding agents to real browsers.

The Dark Arts of Web Automation: Teaching Agents to Use Websites Like Humans (AI Engineer World's Fair 2026, about 22 minutes). Corey Gallon presents a sense, act, verify loop and a three-rung "ladder" that escalates from synthetic JavaScript clicks to trusted Chrome DevTools Protocol input only when a page requires it, and argues for turning explored workflows into reusable code. His CAPTCHA demonstrations ran on infrastructure and accounts he controls; solving CAPTCHAs on services you do not own can violate their terms and the law, so treat the methodology, not those demos, as the takeaway.

Agentic ProbLLMs: Exploiting AI Computer-Use and Coding Agents (39th Chaos Communication Congress, December 2025, about 58 minutes). Security researcher Johann Rehberger walks through end-to-end prompt injection exploits against computer-use and coding agents, drawn from more than two dozen vulnerabilities he disclosed, and discusses mitigations. It explains concretely why agents that browse and act need independent safeguards rather than trust in the model.

Bringing agents onto the world wide web (AI Engineer World's Fair 2026, about 18 minutes). Paul Klein IV, founder of Browserbase, argues that reliable browser agents depend less on raw model capability and more on the harness: selective context, reusable website knowledge, consistent browser environments, combining code with browser interaction, and secure delegated identity. Slightly shorter than this page's usual 20-minute guideline, but included because it summarizes the 2026 production view of browser agents.