{"id":1338,"date":"2026-06-22T12:00:00","date_gmt":"2026-06-22T12:00:00","guid":{"rendered":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/"},"modified":"2026-06-22T12:59:35","modified_gmt":"2026-06-22T12:59:35","slug":"building-browser-using-ai-agents-in-python","status":"publish","type":"post","link":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/","title":{"rendered":"Constructing Browser-Utilizing AI Brokers in Python"},"content":{"rendered":"<p><br \/>\n<\/p>\n<div id=\"\">\n<p>On this article, you&#8217;ll discover ways to construct AI brokers that may browse and work together with actual web sites utilizing Playwright, browser-use, and LangGraph.<\/p>\n<p>Matters we are going to cowl embody:<\/p>\n<p>Why Playwright is the suitable basis for browser automation in 2026, and the way it differs from Selenium.<br \/>\nLearn how to scrape dynamic, JavaScript-rendered pages and full multi-step types reliably.<br \/>\nLearn how to wire browser actions into LangGraph and browser-use brokers, deal with anti-bot detection, handle ready and session persistence, and deploy the lead to Docker.<\/p>\n<div style=\"width: 810px\" class=\"wp-caption aligncenter\"><img fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2026\/06\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python.png\" alt=\"Building Browser-Using AI Agents in Python\" width=\"800\" height=\"706\"\/><\/p>\n<p class=\"wp-caption-text\">Constructing Browser-Utilizing AI Brokers in Python<\/p>\n<\/div>\n<h2>Introduction<\/h2>\n<p>Most AI agent tutorials begin with an API. They present you find out how to name OpenWeather, hit the Stripe endpoint, pull information from GitHub. That may be a tremendous start line till you attempt to construct one thing actual and understand that the duty you really need achieved doesn&#8217;t have an API.<\/p>\n<p>Take into consideration what people do with browsers daily: submitting authorities types, studying competitor pricing, extracting analysis from websites that guard their information behind JavaScript rendering, logging into portals which have by no means heard of OAuth. There are roughly 1.1 billion web sites on the web. A vanishingly small fraction of them have public APIs. The remaining solely converse browser.<\/p>\n<p>An agent that&#8217;s restricted to API calls handles perhaps 5% of the duties a human employee does every day. Give that agent a browser, and the protection approaches every part. That&#8217;s the hole this text closes.<\/p>\n<p>The worldwide AI brokers market stands at $10.91 billion in 2026 and is projected to achieve $50.31 billion by 2030, with browser-capable brokers on the middle of that progress. 27.7% of enterprises are already operating agentic browsers in manufacturing, up from just about none two years prior. The tooling has matured quick, and the patterns are settled sufficient to show correctly.<\/p>\n<p>By the top of this text, you&#8217;ll have a working browser agent that navigates actual web sites, fills types, extracts structured information, and connects to an LLM that decides what to do subsequent, all in Python.<\/p>\n<h2>Why Playwright, Not Selenium<\/h2>\n<p>If you happen to constructed browser automation 5 years in the past, you constructed it with Selenium. Selenium remains to be extensively deployed, nonetheless works, and isn&#8217;t going wherever. However for any new challenge in 2026, Playwright is the default. The explanations are sensible, not theoretical.<\/p>\n<p>Selenium communicates with the browser by sending particular person HTTP requests to a WebDriver. Each motion, click on, sort, scroll, is a separate request. Playwright makes use of a persistent WebSocket connection for the complete session. Instructions move by means of that channel with no per-action round-trip value. Unbiased benchmarks constantly present Playwright operating 30-50% sooner than Selenium on the test-suite stage and averaging ~290ms per motion versus Selenium\u2019s ~536ms. For a browser agent which may execute lots of of actions, that hole compounds.<\/p>\n<p>Playwright additionally bundles its personal browser binaries. Whenever you set up it, you get pre-configured variations of Chromium, Firefox, and WebKit which might be assured to work along with your Playwright model. No driver model mismatches, no damaged CI pipelines as a result of somebody up to date Chrome. It has built-in auto-waiting earlier than it clicks a component; it verifies the component is seen, enabled, and never animating. You do not need to put in writing time.sleep(2) and hope for the most effective.<\/p>\n<p>For AI brokers particularly, Playwright fires actual mouse and keyboard occasions that mirror how people work together with browsers. Websites designed to detect automation search for artificial DOM clicks. Playwright\u2019s interplay mannequin is tougher to tell apart from real human enter.<\/p>\n<p>There&#8217;s additionally the browser-use library, which sits one stage greater. Browser-use is a Python library that offers an LLM a working browser. Underneath the hood, it makes use of Playwright to drive the browser, however the LLM reads the web page state and decides what to click on, sort, and extract, no CSS selectors required. You give it a activity in plain English, and it figures out the remainder. We are going to cowl each uncooked Playwright and browser-use on this article, as a result of they serve totally different wants: Playwright if you need exact, predictable management; browser-use if you need the agent to deal with navigation selections autonomously.<\/p>\n<h2>Setting Up the Atmosphere<\/h2>\n<p>You want Python 3.10 or greater, an OpenAI API key, and about 5 minutes.<\/p>\n<p>Step 1: Create a digital setting<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b2256ba163933016\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\npython -m venv browser_agent_env&#13;<br \/>\n&#13;<br \/>\n# macOS \/ Linux&#13;<br \/>\nsupply browser_agent_env\/bin\/activate&#13;<br \/>\n&#13;<br \/>\n# Home windows&#13;<br \/>\nbrowser_agent_envScriptsactivate<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-v\">python<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-i\">m<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">venv <\/span><span class=\"crayon-v\">browser_agent<\/span><span class=\"crayon-sy\">_<\/span>env<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># macOS \/ Linux<\/span><\/p>\n<p><span class=\"crayon-e\">supply <\/span><span class=\"crayon-v\">browser_agent_env<\/span><span class=\"crayon-o\">\/<\/span><span class=\"crayon-v\">bin<\/span><span class=\"crayon-o\">\/<\/span><span class=\"crayon-i\">activate<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Home windows<\/span><\/p>\n<p><span class=\"crayon-v\">browser_agent_env<\/span><span class=\"crayon-sy\"><\/span><span class=\"crayon-v\">Scripts<\/span><span class=\"crayon-sy\"><\/span><span class=\"crayon-v\">activate<\/span><\/p>\n<\/div><\/div><\/div>\n<p>Step 2: Set up dependencies<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b2256c9124493713\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\npip set up playwright &#13;<br \/>\n            browser-use &#13;<br \/>\n            langchain &#13;<br \/>\n            langchain-openai &#13;<br \/>\n            langgraph &#13;<br \/>\n            langchain-community &#13;<br \/>\n            python-dotenv<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-e\">pip <\/span><span class=\"crayon-e\">set up <\/span><span class=\"crayon-i\">playwright<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\"><\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-st\">use<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\"><\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-i\">langchain<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\"><\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">langchain<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-i\">openai<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\"><\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-i\">langgraph<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\"><\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">langchain<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-i\">neighborhood<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\"><\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">python<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">dotenv<\/span><\/p>\n<\/div><\/div><\/div>\n<p>Step 3: Set up the browser binariesThis is the step most individuals miss. Playwright must obtain Chromium, Firefox, and WebKit individually from the Python bundle. Run this as soon as after putting in:<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b2256ce660457952\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\nplaywright set up chromium<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-e\">playwright <\/span><span class=\"crayon-e\">set up <\/span><span class=\"crayon-v\">chromium<\/span><\/p>\n<\/div><\/div><\/div>\n<p>If you would like all three browser engines: playwright set up. Chromium alone is ample for many agent work and is smaller to obtain.<\/p>\n<p>Step 4: Retailer your API keyCreate a .env file in your challenge listing:<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b2256d2451777129\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\nOPENAI_API_KEY=your_openai_api_key_here<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-v\">OPENAI_API_KEY<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">your_openai_api_key_here<\/span><\/p>\n<\/div><\/div><\/div>\n<p>Add .env to your .gitignore instantly. Don&#8217;t commit API keys.<\/p>\n<p>Step 5: Confirm every part worksHere is a primary script that navigates to a URL, reads the heading, and saves a screenshot. Use instance.com, a publicly out there take a look at area maintained by IANA that won&#8217;t block you.<\/p>\n<p>Learn how to run: Save as first_run.py and run python first_run.py<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b2256d7077513757\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\n# first_run.py&#13;<br \/>\n# Navigate to a URL, take a screenshot, and extract the web page title.&#13;<br \/>\n# Stipulations: pip set up playwright &amp;&amp; playwright set up chromium&#13;<br \/>\n# Learn how to run: python first_run.py&#13;<br \/>\n&#13;<br \/>\nimport asyncio&#13;<br \/>\nfrom playwright.async_api import async_playwright&#13;<br \/>\n&#13;<br \/>\nasync def foremost():&#13;<br \/>\n    async with async_playwright() as p:&#13;<br \/>\n        # Launch Chromium in headless mode (no seen browser window).&#13;<br \/>\n        # Set headless=False if you wish to watch it run throughout growth.&#13;<br \/>\n        browser = await p.chromium.launch(headless=True)&#13;<br \/>\n&#13;<br \/>\n        # A browser context is sort of a recent browser profile.&#13;<br \/>\n        # It isolates cookies, storage, and cache from different contexts.&#13;<br \/>\n        context = await browser.new_context(&#13;<br \/>\n            viewport={&#8220;width&#8221;: 1280, &#8220;peak&#8221;: 720},&#13;<br \/>\n            user_agent=(&#13;<br \/>\n                &#8220;Mozilla\/5.0 (Home windows NT 10.0; Win64; x64) &#8220;&#13;<br \/>\n                &#8220;AppleWebKit\/537.36 (KHTML, like Gecko) &#8220;&#13;<br \/>\n                &#8220;Chrome\/120.0.0.0 Safari\/537.36&#8243;&#13;<br \/>\n            )&#13;<br \/>\n        )&#13;<br \/>\n&#13;<br \/>\n        web page = await context.new_page()&#13;<br \/>\n&#13;<br \/>\n        # Navigate to the URL and wait till the community is idle.&#13;<br \/>\n        # &#8220;networkidle&#8221; means no open community connections for 500ms.&#13;<br \/>\n        # For sooner pages, &#8220;domcontentloaded&#8221; is ample.&#13;<br \/>\n        await web page.goto(&#8220;https:\/\/instance.com&#8221;, wait_until=&#8221;networkidle&#8221;)&#13;<br \/>\n&#13;<br \/>\n        # Extract the web page title&#13;<br \/>\n        title = await web page.title()&#13;<br \/>\n        print(f&#8221;Web page title: {title}&#8221;)&#13;<br \/>\n&#13;<br \/>\n        # Extract the textual content content material of the h1 heading&#13;<br \/>\n        h1 = await web page.text_content(&#8220;h1&#8243;)&#13;<br \/>\n        print(f&#8221;H1 heading: {h1}&#8221;)&#13;<br \/>\n&#13;<br \/>\n        # Take a full-page screenshot and put it aside to disk&#13;<br \/>\n        await web page.screenshot(path=&#8221;screenshot.png&#8221;, full_page=True)&#13;<br \/>\n        print(&#8220;Screenshot saved to screenshot.png&#8221;)&#13;<br \/>\n&#13;<br \/>\n        await browser.shut()&#13;<br \/>\n&#13;<br \/>\nasyncio.run(foremost())<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<p>37<\/p>\n<p>38<\/p>\n<p>39<\/p>\n<p>40<\/p>\n<p>41<\/p>\n<p>42<\/p>\n<p>43<\/p>\n<p>44<\/p>\n<p>45<\/p>\n<p>46<\/p>\n<p>47<\/p>\n<\/div>\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># first_run.py<\/span><\/p>\n<p><span class=\"crayon-p\"># Navigate to a URL, take a screenshot, and extract the web page title.<\/span><\/p>\n<p><span class=\"crayon-p\"># Stipulations: pip set up playwright &amp;&amp; playwright set up chromium<\/span><\/p>\n<p><span class=\"crayon-p\"># Learn how to run: python first_run.py<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">asyncio<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">playwright<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">async_api <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">async_playwright<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">foremost<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">with <\/span><span class=\"crayon-e\">async_playwright<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">as<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">p<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Launch Chromium in headless mode (no seen browser window).<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Set headless=False if you wish to watch it run throughout growth.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">await<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">p<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">chromium<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">launch<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">headless<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-t\">True<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># A browser context is sort of a recent browser profile.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># It isolates cookies, storage, and cache from different contexts.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">new_context<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">viewport<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;width&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">1280<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;peak&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">720<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">user_agent<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Mozilla\/5.0 (Home windows NT 10.0; Win64; x64) &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;AppleWebKit\/537.36 (KHTML, like Gecko) &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Chrome\/120.0.0.0 Safari\/537.36&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">new_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Navigate to the URL and wait till the community is idle.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># &#8220;networkidle&#8221; means no open community connections for 500ms.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># For sooner pages, &#8220;domcontentloaded&#8221; is ample.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-st\">goto<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;https:\/\/instance.com&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">wait_until<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;networkidle&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Extract the web page title<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">title<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">title<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Web page title: {title}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Extract the textual content content material of the h1 heading<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">h1<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">text_content<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;h1&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;H1 heading: {h1}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Take a full-page screenshot and put it aside to disk<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">screenshot<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">path<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;screenshot.png&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">full_page<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-t\">True<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;Screenshot saved to screenshot.png&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">shut<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">asyncio<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">run<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-e\">foremost<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div><\/div><\/div>\n<p>What this does: async_playwright() is the entry level for the complete Playwright session. The browser_context is equal to opening a recent incognito window; cookies, native storage, and cache are remoted from every part else. wait_until=\u201dnetworkidle\u201d tells Playwright to attend till the web page has completed all its community exercise earlier than your code continues, which is the most secure wait technique for dynamic pages.<\/p>\n<p>If this runs and saves a screenshot, your setting is working appropriately.<\/p>\n<h2>Net Navigation and Scraping<\/h2>\n<p>The explanation you want Playwright as an alternative of requests + BeautifulSoup is JavaScript rendering. Trendy web sites ship a skeleton of HTML after which construct the precise content material dynamically after the web page masses: React, Vue, Angular, Subsequent.js. A plain HTTP request fetches the skeleton. Playwright runs an actual browser, so it sees precisely what a human sees in spite of everything JavaScript has executed.<\/p>\n<p>The goal beneath is books.toscrape.com, a authorized scraping sandbox constructed for observe. It paginates outcomes, makes use of dynamic class names for scores, and carefully mirrors the construction of actual e-commerce product pages.<\/p>\n<p>Learn how to run: Save as scrape_books.py and run python scrape_books.py<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b2256dc900439535\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<div class=\"urvanov-syntax-highlighter-plain-wrap\">\n# scrape_books.py&#13;<br \/>\n# Scrape ebook titles, costs, and scores from books.toscrape.com&#13;<br \/>\n# This can be a authorized scraping sandbox website constructed for observe.&#13;<br \/>\n# Stipulations: pip set up playwright &amp;&amp; playwright set up chromium&#13;<br \/>\n# Learn how to run: python scrape_books.py&#13;<br \/>\n&#13;<br \/>\nimport asyncio&#13;<br \/>\nimport json&#13;<br \/>\nfrom playwright.async_api import async_playwright&#13;<br \/>\n&#13;<br \/>\nasync def scrape_books(max_pages: int = 3) -&gt; record[dict]:&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    Scrape ebook listings from books.toscrape.com throughout a number of pages.&#13;<br \/>\n    Returns an inventory of dicts with title, worth, score, and web page quantity.&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    outcomes = []&#13;<br \/>\n&#13;<br \/>\n    async with async_playwright() as p:&#13;<br \/>\n        browser = await p.chromium.launch(headless=True)&#13;<br \/>\n        context = await browser.new_context(viewport={&#8220;width&#8221;: 1280, &#8220;peak&#8221;: 720})&#13;<br \/>\n        web page = await context.new_page()&#13;<br \/>\n&#13;<br \/>\n        for page_num in vary(1, max_pages + 1):&#13;<br \/>\n            url = f&#8221;https:\/\/books.toscrape.com\/catalogue\/page-{page_num}.html&#8221;&#13;<br \/>\n            print(f&#8221;Scraping web page {page_num}: {url}&#8221;)&#13;<br \/>\n&#13;<br \/>\n            await web page.goto(url, wait_until=&#8221;domcontentloaded&#8221;)&#13;<br \/>\n&#13;<br \/>\n            # Look forward to the product playing cards to be seen earlier than extracting.&#13;<br \/>\n            # That is essential on JavaScript-heavy pages the place content material masses after the HTML.&#13;<br \/>\n            # timeout=10000 means wait as much as 10 seconds earlier than elevating an error.&#13;<br \/>\n            await web page.wait_for_selector(&#8220;article.product_pod&#8221;, timeout=10000)&#13;<br \/>\n&#13;<br \/>\n            # Get all ebook playing cards on the present web page&#13;<br \/>\n            books = await web page.query_selector_all(&#8220;article.product_pod&#8221;)&#13;<br \/>\n&#13;<br \/>\n            for ebook in books:&#13;<br \/>\n                # Extract title from the  tag&#8217;s title attribute&#13;<br \/>\n                title_el = await ebook.query_selector(&#8220;h3 a&#8221;)&#13;<br \/>\n                title = await title_el.get_attribute(&#8220;title&#8221;) if title_el else &#8220;N\/A&#8221;&#13;<br \/>\n&#13;<br \/>\n                # Extract worth textual content&#13;<br \/>\n                price_el = await ebook.query_selector(&#8220;.price_color&#8221;)&#13;<br \/>\n                worth = await price_el.inner_text() if price_el else &#8220;N\/A&#8221;&#13;<br \/>\n&#13;<br \/>\n                # Extract star score from the CSS class title.&#13;<br \/>\n                # e.g. <\/p>\n<p class=\"star-rating Three\"> \u2192 &#8220;Three&#8221;&#13;<br \/>\n                rating_el = await ebook.query_selector(&#8220;p.star-rating&#8221;)&#13;<br \/>\n                rating_class = await rating_el.get_attribute(&#8220;class&#8221;) if rating_el else &#8220;&#8221;&#13;<br \/>\n                score = rating_class.change(&#8220;star-rating&#8221;, &#8220;&#8221;).strip()&#13;<br \/>\n&#13;<br \/>\n                outcomes.append({&#13;<br \/>\n                    &#8220;title&#8221;: title,&#13;<br \/>\n                    &#8220;worth&#8221;: worth,&#13;<br \/>\n                    &#8220;score&#8221;: score,&#13;<br \/>\n                    &#8220;web page&#8221;: page_num&#13;<br \/>\n                })&#13;<br \/>\n&#13;<br \/>\n            print(f&#8221;  Extracted {len(books)} books from web page {page_num}&#8221;)&#13;<br \/>\n&#13;<br \/>\n        await browser.shut()&#13;<br \/>\n&#13;<br \/>\n    return outcomes&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\nasync def foremost():&#13;<br \/>\n    books = await scrape_books(max_pages=2)&#13;<br \/>\n    print(f&#8221;nTotal books scraped: {len(books)}&#8221;)&#13;<br \/>\n    print(json.dumps(books[:3], indent=2))&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\nasyncio.run(foremost())<\/p>\n<\/div>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<p>37<\/p>\n<p>38<\/p>\n<p>39<\/p>\n<p>40<\/p>\n<p>41<\/p>\n<p>42<\/p>\n<p>43<\/p>\n<p>44<\/p>\n<p>45<\/p>\n<p>46<\/p>\n<p>47<\/p>\n<p>48<\/p>\n<p>49<\/p>\n<p>50<\/p>\n<p>51<\/p>\n<p>52<\/p>\n<p>53<\/p>\n<p>54<\/p>\n<p>55<\/p>\n<p>56<\/p>\n<p>57<\/p>\n<p>58<\/p>\n<p>59<\/p>\n<p>60<\/p>\n<p>61<\/p>\n<p>62<\/p>\n<p>63<\/p>\n<p>64<\/p>\n<p>65<\/p>\n<p>66<\/p>\n<p>67<\/p>\n<p>68<\/p>\n<p>69<\/p>\n<p>70<\/p>\n<p>71<\/p>\n<p>72<\/p>\n<\/div>\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># scrape_books.py<\/span><\/p>\n<p><span class=\"crayon-p\"># Scrape ebook titles, costs, and scores from books.toscrape.com<\/span><\/p>\n<p><span class=\"crayon-p\"># This can be a authorized scraping sandbox website constructed for observe.<\/span><\/p>\n<p><span class=\"crayon-p\"># Stipulations: pip set up playwright &amp;&amp; playwright set up chromium<\/span><\/p>\n<p><span class=\"crayon-p\"># Learn how to run: python scrape_books.py<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">asyncio<\/span><\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">json<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">playwright<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">async_api <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">async_playwright<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">scrape_books<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">max_pages<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-t\">int<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">3<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">record<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-v\">dict<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Scrape ebook listings from books.toscrape.com throughout a number of pages.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Returns an inventory of dicts with title, worth, score, and web page quantity.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">outcomes<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">with <\/span><span class=\"crayon-e\">async_playwright<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">as<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">p<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">await<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">p<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">chromium<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">launch<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">headless<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-t\">True<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">new_context<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">viewport<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;width&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">1280<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;peak&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">720<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">new_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">page_num <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">vary<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-cn\">1<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">max_pages<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">+<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">1<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">url<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;https:\/\/books.toscrape.com\/catalogue\/page-{page_num}.html&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Scraping web page {page_num}: {url}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-st\">goto<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">url<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">wait_until<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;domcontentloaded&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Look forward to the product playing cards to be seen earlier than extracting.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># That is essential on JavaScript-heavy pages the place content material masses after the HTML.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># timeout=10000 means wait as much as 10 seconds earlier than elevating an error.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">wait_for_selector<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;article.product_pod&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">timeout<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">10000<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Get all ebook playing cards on the present web page<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">books<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">query_selector_all<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;article.product_pod&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">ebook <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">books<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">title_el<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">ebook<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">query_selector<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;h3 a&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">title<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">title_el<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">get_attribute<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;title&#8221;<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">title_el <\/span><span class=\"crayon-st\">else<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;N\/A&#8221;<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Extract worth textual content<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">price_el<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">ebook<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">query_selector<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;.price_color&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">worth<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">price_el<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">inner_text<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">price_el <\/span><span class=\"crayon-st\">else<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;N\/A&#8221;<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Extract star score from the CSS class title.<\/span><\/p>\n<div class=\"crayon-line\" id=\"urvanov-syntax-highlighter-6a3931b2256dc900439535-47\"><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># e.g. <\/p>\n<p class=\"star-rating Three\"> \u2192 &#8220;Three&#8221;<\/p>\n<p><\/span><\/div>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">rating_el<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">ebook<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">query_selector<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;p.star-rating&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">rating_class<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">rating_el<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">get_attribute<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;class&#8221;<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">rating_el <\/span><span class=\"crayon-st\">else<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">score<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">rating_class<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">change<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;star-rating&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">strip<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">outcomes<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">append<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;title&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">title<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;worth&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">worth<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;score&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">score<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;web page&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">_<\/span>num<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8221;\u00a0\u00a0Extracted {len(books)} books from web page {page_num}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">shut<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">outcomes<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">foremost<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">books<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">scrape_books<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">max_pages<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">2<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;nTotal books scraped: {len(books)}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">json<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">dumps<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">books<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-cn\">3<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">indent<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">2<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">asyncio<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">run<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-e\">foremost<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div><\/div><\/div>\n<p>What this does: wait_for_selector() is the important thing name right here. As an alternative of sleeping for a hard and fast time and hoping the content material has loaded, it watches the DOM and proceeds the second the goal component seems, or raises a TimeoutError if it doesn&#8217;t seem inside the timeout window. That&#8217;s the proper habits: fail quick and explicitly relatively than silently extracting from an empty web page.<\/p>\n<p>The score extraction deserves consideration. The star score is encoded as a CSS class (star-rating Three), not a quantity. The code strips \u201cstar-rating\u201d from the category string to get the textual content worth. That is the sort of factor you solely know by inspecting the precise HTML. Whenever you hand this activity to a uncooked LLM with no browser, it has no technique to know what the category construction appears like. With Playwright, you possibly can examine it instantly and extract it precisely.<\/p>\n<h2>Kind Completion and Multi-Step Flows<\/h2>\n<p>Filling types is the place browser brokers earn their maintain and the place most automation scripts fail. The reason being that net types will not be simply inputs and buttons. They fireplace focus, enter, change, and blur occasions in sequence. JavaScript validation listens for these occasions. If you happen to inject a price into an enter subject by instantly setting worth within the DOM (as older automation instruments usually do), the validation listeners by no means fireplace and the shape breaks.<\/p>\n<p>Playwright\u2019s fill() and click on() strategies fireplace actual browser occasions in the suitable order, which is why they work on type validation that might block lower-level approaches.<\/p>\n<p>The goal beneath is the-internet.herokuapp.com\/login, a public take a look at website maintained particularly for automation observe. It accepts tomsmith \/ SuperSecretPassword! as legitimate credentials and returns clear success\/failure messages.<\/p>\n<p>Learn how to run: Save as form_submit.py and run python form_submit.py<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b2256e1597964268\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\n# form_submit.py&#13;<br \/>\n# Full and submit a multi-field login type on a public demo website.&#13;<br \/>\n# Goal: https:\/\/the-internet.herokuapp.com\/login (public take a look at website)&#13;<br \/>\n# Stipulations: pip set up playwright &amp;&amp; playwright set up chromium&#13;<br \/>\n# Learn how to run: python form_submit.py&#13;<br \/>\n&#13;<br \/>\nimport asyncio&#13;<br \/>\nfrom playwright.async_api import async_playwright&#13;<br \/>\n&#13;<br \/>\nasync def login_and_verify(username: str, password: str) -&gt; dict:&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    Try to log in to a demo website and return whether or not it succeeded.&#13;<br \/>\n    Handles: enter filling, button clicking, and outcome verification.&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    async with async_playwright() as p:&#13;<br \/>\n        browser = await p.chromium.launch(headless=True)&#13;<br \/>\n        context = await browser.new_context()&#13;<br \/>\n        web page = await context.new_page()&#13;<br \/>\n&#13;<br \/>\n        await web page.goto(&#8220;https:\/\/the-internet.herokuapp.com\/login&#8221;)&#13;<br \/>\n&#13;<br \/>\n        # Look forward to the shape to be seen earlier than interacting.&#13;<br \/>\n        # state=&#8221;seen&#8221; is the default however makes the intent express.&#13;<br \/>\n        await web page.wait_for_selector(&#8220;#username&#8221;, state=&#8221;seen&#8221;)&#13;<br \/>\n&#13;<br \/>\n        # fill() clears the sector first, then varieties the worth.&#13;<br \/>\n        # It fires the main focus, enter, and alter occasions so as.&#13;<br \/>\n        await web page.fill(&#8220;#username&#8221;, username)&#13;<br \/>\n        await web page.fill(&#8220;#password&#8221;, password)&#13;<br \/>\n&#13;<br \/>\n        # click on() fires actual mouse occasions &#8212; mousedown, mouseup, click on.&#13;<br \/>\n        # This triggers JavaScript listeners {that a} plain DOM click on misses.&#13;<br \/>\n        await web page.click on(&#8220;button[type=&#8221;submit&#8221;]&#8221;)&#13;<br \/>\n&#13;<br \/>\n        # Look forward to the web page to settle after type submission&#13;<br \/>\n        await web page.wait_for_load_state(&#8220;networkidle&#8221;)&#13;<br \/>\n&#13;<br \/>\n        # Test which outcome component appeared&#13;<br \/>\n        success_el = await web page.query_selector(&#8220;.flash.success&#8221;)&#13;<br \/>\n        error_el = await web page.query_selector(&#8220;.flash.error&#8221;)&#13;<br \/>\n&#13;<br \/>\n        if success_el:&#13;<br \/>\n            message = await success_el.inner_text()&#13;<br \/>\n            outcome = {&#8220;success&#8221;: True, &#8220;message&#8221;: message.strip()}&#13;<br \/>\n        elif error_el:&#13;<br \/>\n            message = await error_el.inner_text()&#13;<br \/>\n            outcome = {&#8220;success&#8221;: False, &#8220;message&#8221;: message.strip()}&#13;<br \/>\n        else:&#13;<br \/>\n            outcome = {&#8220;success&#8221;: False, &#8220;message&#8221;: &#8220;Unknown outcome&#8221;}&#13;<br \/>\n&#13;<br \/>\n        await browser.shut()&#13;<br \/>\n        return outcome&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\nasync def foremost():&#13;<br \/>\n    # Legitimate credentials for the demo website&#13;<br \/>\n    outcome = await login_and_verify(&#8220;tomsmith&#8221;, &#8220;SuperSecretPassword!&#8221;)&#13;<br \/>\n    print(f&#8221;Legitimate login:   {outcome}&#8221;)&#13;<br \/>\n&#13;<br \/>\n    # Invalid credentials to confirm error dealing with&#13;<br \/>\n    result_fail = await login_and_verify(&#8220;wronguser&#8221;, &#8220;wrongpass&#8221;)&#13;<br \/>\n    print(f&#8221;Invalid login: {result_fail}&#8221;)&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\nasyncio.run(foremost())<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<p>37<\/p>\n<p>38<\/p>\n<p>39<\/p>\n<p>40<\/p>\n<p>41<\/p>\n<p>42<\/p>\n<p>43<\/p>\n<p>44<\/p>\n<p>45<\/p>\n<p>46<\/p>\n<p>47<\/p>\n<p>48<\/p>\n<p>49<\/p>\n<p>50<\/p>\n<p>51<\/p>\n<p>52<\/p>\n<p>53<\/p>\n<p>54<\/p>\n<p>55<\/p>\n<p>56<\/p>\n<p>57<\/p>\n<p>58<\/p>\n<p>59<\/p>\n<p>60<\/p>\n<p>61<\/p>\n<p>62<\/p>\n<p>63<\/p>\n<p>64<\/p>\n<p>65<\/p>\n<\/div>\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># form_submit.py<\/span><\/p>\n<p><span class=\"crayon-p\"># Full and submit a multi-field login type on a public demo website.<\/span><\/p>\n<p><span class=\"crayon-p\"># Goal: https:\/\/the-internet.herokuapp.com\/login (public take a look at website)<\/span><\/p>\n<p><span class=\"crayon-p\"># Stipulations: pip set up playwright &amp;&amp; playwright set up chromium<\/span><\/p>\n<p><span class=\"crayon-p\"># Learn how to run: python form_submit.py<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">asyncio<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">playwright<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">async_api <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">async_playwright<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">login_and_verify<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">username<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">password<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">dict<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Try to log in to a demo website and return whether or not it succeeded.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Handles: enter filling, button clicking, and outcome verification.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">with <\/span><span class=\"crayon-e\">async_playwright<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">as<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">p<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">await<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">p<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">chromium<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">launch<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">headless<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-t\">True<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">new_context<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">new_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-st\">goto<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;https:\/\/the-internet.herokuapp.com\/login&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Look forward to the shape to be seen earlier than interacting.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># state=&#8221;seen&#8221; is the default however makes the intent express.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">wait_for_selector<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;#username&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">state<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;seen&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># fill() clears the sector first, then varieties the worth.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># It fires the main focus, enter, and alter occasions so as.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">fill<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;#username&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">username<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">fill<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;#password&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">password<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># click on() fires actual mouse occasions &#8212; mousedown, mouseup, click on.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># This triggers JavaScript listeners {that a} plain DOM click on misses.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">click on<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;button[type=&#8221;submit&#8221;]&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Look forward to the web page to settle after type submission<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">wait_for_load_state<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;networkidle&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Test which outcome component appeared<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">success_el<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">query_selector<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;.flash.success&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">error_el<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">query_selector<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;.flash.error&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">success_el<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">message<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">success_el<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">inner_text<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">outcome<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;success&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-t\">True<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;message&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">message<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">strip<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">elif <\/span><span class=\"crayon-v\">error_el<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">message<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">error_el<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">inner_text<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">outcome<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;success&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-t\">False<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;message&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">message<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">strip<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">else<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">outcome<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;success&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-t\">False<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;message&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;Unknown outcome&#8221;<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">shut<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">outcome<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">foremost<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Legitimate credentials for the demo website<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">outcome<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">login_and_verify<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;tomsmith&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;SuperSecretPassword!&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Legitimate login:\u00a0\u00a0 {outcome}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Invalid credentials to confirm error dealing with<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">result_fail<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">login_and_verify<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;wronguser&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;wrongpass&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Invalid login: {result_fail}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">asyncio<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">run<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-e\">foremost<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div><\/div><\/div>\n<p>What this does: The sample right here, fill() \u2192 click on() \u2192 wait_for_load_state() \u2192 examine for outcome component, is the template for nearly any type interplay. The wait_for_load_state(\u201cnetworkidle\u201d) after the submit is essential: with out it, you question the DOM earlier than the web page has up to date and get the pre-submission state, not the outcome.<\/p>\n<p>For extra advanced types with file uploads, dropdowns, and checkboxes:<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b2256ef751558372\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\n# File add&#13;<br \/>\nawait web page.set_input_files(&#8220;#file-upload&#8221;, &#8220;\/path\/to\/doc.pdf&#8221;)&#13;<br \/>\n&#13;<br \/>\n# Choose dropdown by seen label textual content&#13;<br \/>\nawait web page.select_option(&#8220;#country-select&#8221;, label=&#8221;Nigeria&#8221;)&#13;<br \/>\n&#13;<br \/>\n# Test a checkbox&#13;<br \/>\nawait web page.examine(&#8220;#agree-terms&#8221;)&#13;<br \/>\n&#13;<br \/>\n# Deal with a modal dialog (affirm\/alert)&#13;<br \/>\nweb page.on(&#8220;dialog&#8221;, lambda dialog: asyncio.ensure_future(dialog.settle for()))<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># File add<\/span><\/p>\n<p><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">set_input_files<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;#file-upload&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;\/path\/to\/doc.pdf&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Choose dropdown by seen label textual content<\/span><\/p>\n<p><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">select_option<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;#country-select&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">label<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;Nigeria&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Test a checkbox<\/span><\/p>\n<p><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">examine<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;#agree-terms&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Deal with a modal dialog (affirm\/alert)<\/span><\/p>\n<p><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">on<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;dialog&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">lambda <\/span><span class=\"crayon-v\">dialog<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">asyncio<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">ensure_future<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">dialog<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">settle for<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div><\/div><\/div>\n<h2>Instrument Orchestration with LangChain and LangGraph<\/h2>\n<p>Uncooked Playwright scripts are highly effective however fastened. They do precisely what you coded, no extra. The second a web page adjustments its construction, or the duty requires a call the script didn&#8217;t anticipate, it breaks.<\/p>\n<p>Connecting Playwright to an LLM adjustments this. Browser actions develop into instruments the agent can name when it decides they&#8217;re wanted. The agent reads the duty, causes about what to do, calls a instrument, reads the outcome, and decides what to do subsequent. That loop handles variation {that a} fastened script can&#8217;t.<\/p>\n<p>That is the bridge from \u201cbrowser automation script\u201d to \u201cAI agent.\u201d<\/p>\n<p>Learn how to run: Save as agent_tools.py, guarantee OPENAI_API_KEY is in your .env, then run python agent_tools.py<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b2256f4668403934\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\n# agent_tools.py&#13;<br \/>\n# LangGraph agent with three browser instruments: navigate_and_extract, fill_and_submit_form, take_screenshot&#13;<br \/>\n# Stipulations: pip set up playwright langchain langchain-openai langgraph python-dotenv&#13;<br \/>\n#                playwright set up chromium&#13;<br \/>\n# Learn how to run: python agent_tools.py&#13;<br \/>\n&#13;<br \/>\nimport asyncio&#13;<br \/>\nimport os&#13;<br \/>\nfrom dotenv import load_dotenv&#13;<br \/>\nfrom langchain_openai import ChatOpenAI&#13;<br \/>\nfrom langchain.instruments import instrument&#13;<br \/>\nfrom langchain_core.messages import HumanMessage&#13;<br \/>\nfrom langgraph.prebuilt import create_react_agent&#13;<br \/>\nfrom playwright.async_api import async_playwright&#13;<br \/>\n&#13;<br \/>\nload_dotenv()&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 SHARED BROWSER STATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# We maintain a single browser occasion alive for the agent&#8217;s lifetime.&#13;<br \/>\n# Creating and destroying a browser on each instrument name is gradual and wasteful.&#13;<br \/>\n_browser = None&#13;<br \/>\n_page = None&#13;<br \/>\n_playwright = None&#13;<br \/>\n&#13;<br \/>\nasync def get_page():&#13;<br \/>\n    &#8220;&#8221;&#8221;Return the shared web page, launching the browser if wanted.&#8221;&#8221;&#8221;&#13;<br \/>\n    international _browser, _page, _playwright&#13;<br \/>\n    if _browser is None:&#13;<br \/>\n        _playwright = await async_playwright().begin()&#13;<br \/>\n        _browser = await _playwright.chromium.launch(headless=True)&#13;<br \/>\n        context = await _browser.new_context(viewport={&#8220;width&#8221;: 1280, &#8220;peak&#8221;: 720})&#13;<br \/>\n        _page = await context.new_page()&#13;<br \/>\n    return _page&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\nasync def close_browser():&#13;<br \/>\n    &#8220;&#8221;&#8221;Clear up browser assets when the agent session ends.&#8221;&#8221;&#8221;&#13;<br \/>\n    international _browser, _page, _playwright&#13;<br \/>\n    if _browser:&#13;<br \/>\n        await _browser.shut()&#13;<br \/>\n        await _playwright.cease()&#13;<br \/>\n        _browser = None&#13;<br \/>\n        _page = None&#13;<br \/>\n        _playwright = None&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 BROWSER TOOLS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# Word: these are async instruments (async def). LangChain&#8217;s @instrument decorator helps&#13;<br \/>\n# async features instantly, and the agent should be invoked with ainvoke() in order that&#13;<br \/>\n# instrument calls run on the identical occasion loop as an alternative of attempting to begin a second one.&#13;<br \/>\n&#13;<br \/>\n@instrument&#13;<br \/>\nasync def navigate_and_extract(url: str) -&gt; str:&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    Navigate to a URL and return the seen textual content content material of the web page.&#13;<br \/>\n    Use this to go to web sites and skim their content material.&#13;<br \/>\n    Enter: a full URL string together with https:\/\/ (e.g., &#8216;https:\/\/instance.com&#8217;).&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    web page = await get_page()&#13;<br \/>\n    await web page.goto(url, wait_until=&#8221;domcontentloaded&#8221;, timeout=15000)&#13;<br \/>\n    await web page.wait_for_load_state(&#8220;networkidle&#8221;)&#13;<br \/>\n    content material = await web page.inner_text(&#8220;physique&#8221;)&#13;<br \/>\n    # Truncate to keep away from flooding the LLM context window&#13;<br \/>\n    return content material[:3000] if len(content material) &gt; 3000 else content material&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n@instrument&#13;<br \/>\nasync def fill_and_submit_form(selector_value_pairs: str) -&gt; str:&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    Fill type fields and submit a type on the at the moment loaded web page.&#13;<br \/>\n    Enter: a comma-separated string of &#8216;selector:worth&#8217; pairs ending with &#8216;submit:button_selector&#8217;.&#13;<br \/>\n    Instance: &#8216;#e-mail:consumer@instance.com,#password:secret,submit:button[type=submit]&#8217;&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    web page = await get_page()&#13;<br \/>\n    strive:&#13;<br \/>\n        pairs = selector_value_pairs.break up(&#8220;,&#8221;)&#13;<br \/>\n        submit_selector = None&#13;<br \/>\n&#13;<br \/>\n        for pair in pairs:&#13;<br \/>\n            key, val = pair.break up(&#8220;:&#8221;, 1)&#13;<br \/>\n            key = key.strip()&#13;<br \/>\n            val = val.strip()&#13;<br \/>\n            if key == &#8220;submit&#8221;:&#13;<br \/>\n                submit_selector = val&#13;<br \/>\n            else:&#13;<br \/>\n                await web page.fill(key, val)&#13;<br \/>\n&#13;<br \/>\n        if submit_selector:&#13;<br \/>\n            await web page.click on(submit_selector)&#13;<br \/>\n            await web page.wait_for_load_state(&#8220;networkidle&#8221;)&#13;<br \/>\n&#13;<br \/>\n        return f&#8221;Kind submitted. Present URL: {web page.url}&#8221;&#13;<br \/>\n    besides Exception as e:&#13;<br \/>\n        return f&#8221;Kind interplay failed: {str(e)}&#8221;&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n@instrument&#13;<br \/>\nasync def take_screenshot(filename: str) -&gt; str:&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    Take a screenshot of the present browser web page and put it aside to a file.&#13;<br \/>\n    Use this to visually confirm the present state of the web page.&#13;<br \/>\n    Enter: filename string (e.g., &#8216;outcome.png&#8217;).&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    web page = await get_page()&#13;<br \/>\n    await web page.screenshot(path=filename, full_page=False)&#13;<br \/>\n    return f&#8221;Screenshot saved to {filename}&#8221;&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 AGENT SETUP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n&#13;<br \/>\nllm = ChatOpenAI(&#13;<br \/>\n    mannequin=&#8221;gpt-4o&#8221;,&#13;<br \/>\n    temperature=0,&#13;<br \/>\n    api_key=os.getenv(&#8220;OPENAI_API_KEY&#8221;)&#13;<br \/>\n)&#13;<br \/>\n&#13;<br \/>\ninstruments = [navigate_and_extract, fill_and_submit_form, take_screenshot]&#13;<br \/>\n&#13;<br \/>\n# create_react_agent wires collectively the LLM, the instruments, and the ReAct reasoning loop.&#13;<br \/>\n# The agent decides which instrument to name, calls it, reads the outcome, and continues.&#13;<br \/>\nagent = create_react_agent(llm, instruments)&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 DEMO \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n&#13;<br \/>\nasync def foremost():&#13;<br \/>\n    outcome = await agent.ainvoke({&#13;<br \/>\n        &#8220;messages&#8221;: [HumanMessage(&#13;<br \/>\n            content=(&#13;<br \/>\n                &#8220;Go to https:\/\/example.com, read the page content, &#8220;&#13;<br \/>\n                &#8220;then take a screenshot called example.png&#8221;&#13;<br \/>\n            )&#13;<br \/>\n        )]&#13;<br \/>\n    })&#13;<br \/>\n    print(outcome[&#8220;messages&#8221;][-1].content material)&#13;<br \/>\n    await close_browser()&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\nasyncio.run(foremost())<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<p>37<\/p>\n<p>38<\/p>\n<p>39<\/p>\n<p>40<\/p>\n<p>41<\/p>\n<p>42<\/p>\n<p>43<\/p>\n<p>44<\/p>\n<p>45<\/p>\n<p>46<\/p>\n<p>47<\/p>\n<p>48<\/p>\n<p>49<\/p>\n<p>50<\/p>\n<p>51<\/p>\n<p>52<\/p>\n<p>53<\/p>\n<p>54<\/p>\n<p>55<\/p>\n<p>56<\/p>\n<p>57<\/p>\n<p>58<\/p>\n<p>59<\/p>\n<p>60<\/p>\n<p>61<\/p>\n<p>62<\/p>\n<p>63<\/p>\n<p>64<\/p>\n<p>65<\/p>\n<p>66<\/p>\n<p>67<\/p>\n<p>68<\/p>\n<p>69<\/p>\n<p>70<\/p>\n<p>71<\/p>\n<p>72<\/p>\n<p>73<\/p>\n<p>74<\/p>\n<p>75<\/p>\n<p>76<\/p>\n<p>77<\/p>\n<p>78<\/p>\n<p>79<\/p>\n<p>80<\/p>\n<p>81<\/p>\n<p>82<\/p>\n<p>83<\/p>\n<p>84<\/p>\n<p>85<\/p>\n<p>86<\/p>\n<p>87<\/p>\n<p>88<\/p>\n<p>89<\/p>\n<p>90<\/p>\n<p>91<\/p>\n<p>92<\/p>\n<p>93<\/p>\n<p>94<\/p>\n<p>95<\/p>\n<p>96<\/p>\n<p>97<\/p>\n<p>98<\/p>\n<p>99<\/p>\n<p>100<\/p>\n<p>101<\/p>\n<p>102<\/p>\n<p>103<\/p>\n<p>104<\/p>\n<p>105<\/p>\n<p>106<\/p>\n<p>107<\/p>\n<p>108<\/p>\n<p>109<\/p>\n<p>110<\/p>\n<p>111<\/p>\n<p>112<\/p>\n<p>113<\/p>\n<p>114<\/p>\n<p>115<\/p>\n<p>116<\/p>\n<p>117<\/p>\n<p>118<\/p>\n<p>119<\/p>\n<p>120<\/p>\n<p>121<\/p>\n<p>122<\/p>\n<p>123<\/p>\n<p>124<\/p>\n<p>125<\/p>\n<p>126<\/p>\n<p>127<\/p>\n<p>128<\/p>\n<p>129<\/p>\n<p>130<\/p>\n<p>131<\/p>\n<p>132<\/p>\n<p>133<\/p>\n<p>134<\/p>\n<p>135<\/p>\n<p>136<\/p>\n<p>137<\/p>\n<p>138<\/p>\n<p>139<\/p>\n<\/div>\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># agent_tools.py<\/span><\/p>\n<p><span class=\"crayon-p\"># LangGraph agent with three browser instruments: navigate_and_extract, fill_and_submit_form, take_screenshot<\/span><\/p>\n<p><span class=\"crayon-p\"># Stipulations: pip set up playwright langchain langchain-openai langgraph python-dotenv<\/span><\/p>\n<p><span class=\"crayon-p\">#\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0playwright set up chromium<\/span><\/p>\n<p><span class=\"crayon-p\"># Learn how to run: python agent_tools.py<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">asyncio<\/span><\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">os<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">dotenv <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">load_dotenv<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">langchain_openai <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">ChatOpenAI<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langchain<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">instruments <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">instrument<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langchain_core<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">messages <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">HumanMessage<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langgraph<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">prebuilt <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">create_react_agent<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">playwright<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">async_api <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">async_playwright<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">load_dotenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 SHARED BROWSER STATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># We maintain a single browser occasion alive for the agent&#8217;s lifetime.<\/span><\/p>\n<p><span class=\"crayon-p\"># Creating and destroying a browser on each instrument name is gradual and wasteful.<\/span><\/p>\n<p><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">None<\/span><\/p>\n<p><span class=\"crayon-v\">_page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">None<\/span><\/p>\n<p><span class=\"crayon-v\">_playwright<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">None<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">get_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;Return the shared web page, launching the browser if wanted.&#8221;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-m\">international<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">_page<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">_playwright<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">_browser <\/span><span class=\"crayon-st\">is<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">None<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">_playwright<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">async_playwright<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">begin<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">_playwright<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">chromium<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">launch<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">headless<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-t\">True<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">new_context<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">viewport<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;width&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">1280<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;peak&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">720<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">_page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">new_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">_page<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">close_browser<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;Clear up browser assets when the agent session ends.&#8221;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-m\">international<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">_page<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">_playwright<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">shut<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">_playwright<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">cease<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">None<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">_page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">None<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">_playwright<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">None<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 BROWSER TOOLS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># Word: these are async instruments (async def). LangChain&#8217;s @instrument decorator helps<\/span><\/p>\n<p><span class=\"crayon-p\"># async features instantly, and the agent should be invoked with ainvoke() in order that<\/span><\/p>\n<p><span class=\"crayon-p\"># instrument calls run on the identical occasion loop as an alternative of attempting to begin a second one.<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-sy\">@<\/span><span class=\"crayon-e\">instrument<\/span><\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">navigate_and_extract<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">url<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Navigate to a URL and return the seen textual content content material of the web page.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Use this to go to web sites and skim their content material.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Enter: a full URL string together with https:\/\/ (e.g., &#8216;https:\/\/instance.com&#8217;).<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">get_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-st\">goto<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">url<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">wait_until<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;domcontentloaded&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">timeout<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">15000<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">wait_for_load_state<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;networkidle&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">content material<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">inner_text<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;physique&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Truncate to keep away from flooding the LLM context window<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">content material<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-cn\">3000<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">len<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">content material<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">3000<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">else<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">content material<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-sy\">@<\/span><span class=\"crayon-e\">instrument<\/span><\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">fill_and_submit_form<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">selector_value_pairs<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Fill type fields and submit a type on the at the moment loaded web page.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Enter: a comma-separated string of &#8216;selector:worth&#8217; pairs ending with &#8216;submit:button_selector&#8217;.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Instance: &#8216;#e-mail:consumer@instance.com,#password:secret,submit:button[type=submit]&#8217;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">get_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">strive<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">pairs<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">selector_value_pairs<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">break up<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;,&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">submit_selector<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">None<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">pair <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">pairs<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">key<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">val<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">pair<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">break up<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;:&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">1<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">key<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">key<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">strip<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">val<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">val<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">strip<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">key<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">==<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;submit&#8221;<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">submit_selector<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">val<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">else<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">fill<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">key<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">val<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">submit_selector<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">click on<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">submit_selector<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">wait_for_load_state<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;networkidle&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Kind submitted. Present URL: {web page.url}&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">besides <\/span><span class=\"crayon-e\">Exception <\/span><span class=\"crayon-st\">as<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">e<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Kind interplay failed: {str(e)}&#8221;<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-sy\">@<\/span><span class=\"crayon-e\">instrument<\/span><\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">take_screenshot<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">filename<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Take a screenshot of the present browser web page and put it aside to a file.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Use this to visually confirm the present state of the web page.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Enter: filename string (e.g., &#8216;outcome.png&#8217;).<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">get_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">screenshot<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">path<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">filename<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">full_page<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-t\">False<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Screenshot saved to {filename}&#8221;<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 AGENT SETUP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">llm<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">ChatOpenAI<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">mannequin<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;gpt-4o&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">temperature<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">api_key<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">os<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">getenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;OPENAI_API_KEY&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">instruments<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-v\">navigate_and_extract<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">fill_and_submit_form<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">take_screenshot<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># create_react_agent wires collectively the LLM, the instruments, and the ReAct reasoning loop.<\/span><\/p>\n<p><span class=\"crayon-p\"># The agent decides which instrument to name, calls it, reads the outcome, and continues.<\/span><\/p>\n<p><span class=\"crayon-v\">agent<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">create_react_agent<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">llm<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">instruments<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 DEMO \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">foremost<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">outcome<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">agent<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">ainvoke<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;messages&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-e\">HumanMessage<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">content<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Go to https:\/\/example.com, read the page content, &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;then take a screenshot called example.png&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">outcome<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-s\">&#8220;messages&#8221;<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-cn\">1<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">content material<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">close_browser<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">asyncio<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">run<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-e\">foremost<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div><\/div><\/div>\n<p>What this does: The three @tool-decorated features are registered with the agent. Every docstring is what the LLM reads to know what the instrument does and when to make use of it. Write them like job descriptions, not code feedback. The shared _browser and _page globals imply the browser stays open throughout a number of instrument calls, which is crucial for duties that span a number of pages in the identical session. As a result of the instruments are outlined with async def, the agent is invoked with ainvoke() relatively than invoke(), so the instrument calls run on the identical occasion loop that foremost() is already utilizing.<\/p>\n<div style=\"width: 810px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2026\/06\/MLM-Shittu-A-vertical-flow-diagram-showing-how-a-task-request-flows-through-the-agent.png\" alt=\"A vertical flow diagram showing how a task request flows through the agent\" width=\"800\" height=\"706\"\/><\/p>\n<p class=\"wp-caption-text\">A vertical move diagram displaying how a activity request flows by means of the agent (click on to enlarge)Picture by Editor<\/p>\n<\/div>\n<p>The important thing design choice on this snippet is the shared browser occasion. If every instrument name launched and closed its personal browser, you&#8217;d lose all session state between calls, akin to cookies, navigation historical past, and any type state the agent had already constructed up. Maintaining the browser alive for the complete agent session preserves that context.<\/p>\n<h2>Utilizing browser-use for Excessive-Degree Agent Duties<\/h2>\n<p>Uncooked Playwright with @instrument features provides you exact management. The trade-off is that you&#8217;re nonetheless writing selectors, nonetheless desirous about web page construction, nonetheless dealing with each edge case manually. If the positioning adjustments its HTML, your selectors break.<\/p>\n<p>browser-use takes a distinct method. As an alternative of writing selectors, you give the agent a activity in plain English. browser-use makes use of Playwright below the hood, however the LLM reads the present web page state on every step and decides what to do subsequent: which component to click on, what to sort, and when the duty is full. The web page construction shouldn&#8217;t be hardcoded into your code. The agent figures it out at runtime.<\/p>\n<p>browser-use is a Python library that offers an LLM a working browser. The LLM reads every web page and decides what to click on, sort, and extract. This makes it resilient to website adjustments that might break a selector-based script.<\/p>\n<p>When to make use of browser-use over uncooked Playwright:<\/p>\n<p>If the duty is exploratory and the web page construction is unpredictable, use browser-use.<br \/>\nIn case you are operating a hard and fast, repeatable workflow the place each selector is thought and secure, uncooked Playwright is extra dependable and cheaper per run.<br \/>\nA browser-use agent makes a number of LLM calls per activity step; a scripted Playwright run makes none.<\/p>\n<p>Learn how to run: Save as browser_use_agent.py, guarantee OPENAI_API_KEY is in your .env, then run python browser_use_agent.py<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b2256f9181101907\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\n# browser_use_agent.py&#13;<br \/>\n# A browser-use agent that accepts a pure language activity and completes it&#13;<br \/>\n# with none CSS selectors or hardcoded web page construction.&#13;<br \/>\n# Stipulations: pip set up browser-use playwright python-dotenv&#13;<br \/>\n#                playwright set up chromium&#13;<br \/>\n# Learn how to run: python browser_use_agent.py&#13;<br \/>\n&#13;<br \/>\nimport asyncio&#13;<br \/>\nimport os&#13;<br \/>\nfrom dotenv import load_dotenv&#13;<br \/>\nfrom langchain_openai import ChatOpenAI&#13;<br \/>\nfrom browser_use import Agent&#13;<br \/>\n&#13;<br \/>\nload_dotenv()&#13;<br \/>\n&#13;<br \/>\nasync def run_browser_task(activity: str) -&gt; str:&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    Hand a pure language activity to a browser-use agent.&#13;<br \/>\n    The agent handles navigation, clicks, and extraction with out selectors.&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    # temperature=0 retains selections deterministic and reduces hallucinated actions&#13;<br \/>\n    llm = ChatOpenAI(&#13;<br \/>\n        mannequin=&#8221;gpt-4o&#8221;,&#13;<br \/>\n        temperature=0,&#13;<br \/>\n        api_key=os.getenv(&#8220;OPENAI_API_KEY&#8221;)&#13;<br \/>\n    )&#13;<br \/>\n&#13;<br \/>\n    # Agent wraps the browser, the LLM, and the duty loop collectively.&#13;<br \/>\n    # max_actions_per_step limits what number of actions the agent takes earlier than&#13;<br \/>\n    # re-reading the web page &#8212; prevents runaway loops on advanced pages.&#13;<br \/>\n    agent = Agent(&#13;<br \/>\n        activity=activity,&#13;<br \/>\n        llm=llm,&#13;<br \/>\n        max_actions_per_step=5&#13;<br \/>\n    )&#13;<br \/>\n&#13;<br \/>\n    # run() executes the complete activity loop:&#13;<br \/>\n    # learn web page \u2192 determine motion \u2192 take motion \u2192 learn up to date web page \u2192 repeat&#13;<br \/>\n    outcome = await agent.run()&#13;<br \/>\n&#13;<br \/>\n    # final_result() returns the agent&#8217;s extracted content material or conclusion&#13;<br \/>\n    return outcome.final_result() or &#8220;Process accomplished with no extracted output.&#8221;&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\nasync def foremost():&#13;<br \/>\n    activity = (&#13;<br \/>\n        &#8220;Go to https:\/\/books.toscrape.com and discover the three costliest books &#8220;&#13;<br \/>\n        &#8220;on the primary web page. Return their titles and costs.&#8221;&#13;<br \/>\n    )&#13;<br \/>\n    print(f&#8221;Process: {activity}n&#8221;)&#13;<br \/>\n    output = await run_browser_task(activity)&#13;<br \/>\n    print(f&#8221;Outcome:n{output}&#8221;)&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\nasyncio.run(foremost())<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<p>37<\/p>\n<p>38<\/p>\n<p>39<\/p>\n<p>40<\/p>\n<p>41<\/p>\n<p>42<\/p>\n<p>43<\/p>\n<p>44<\/p>\n<p>45<\/p>\n<p>46<\/p>\n<p>47<\/p>\n<p>48<\/p>\n<p>49<\/p>\n<p>50<\/p>\n<p>51<\/p>\n<p>52<\/p>\n<p>53<\/p>\n<p>54<\/p>\n<p>55<\/p>\n<\/div>\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># browser_use_agent.py<\/span><\/p>\n<p><span class=\"crayon-p\"># A browser-use agent that accepts a pure language activity and completes it<\/span><\/p>\n<p><span class=\"crayon-p\"># with none CSS selectors or hardcoded web page construction.<\/span><\/p>\n<p><span class=\"crayon-p\"># Stipulations: pip set up browser-use playwright python-dotenv<\/span><\/p>\n<p><span class=\"crayon-p\">#\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0playwright set up chromium<\/span><\/p>\n<p><span class=\"crayon-p\"># Learn how to run: python browser_use_agent.py<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">asyncio<\/span><\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">os<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">dotenv <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">load_dotenv<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">langchain_openai <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">ChatOpenAI<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">browser_use <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">Agent<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">load_dotenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">run_browser_task<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">activity<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Hand a pure language activity to a browser-use agent.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0The agent handles navigation, clicks, and extraction with out selectors.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># temperature=0 retains selections deterministic and reduces hallucinated actions<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">llm<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">ChatOpenAI<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">mannequin<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;gpt-4o&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">temperature<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">api_key<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">os<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">getenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;OPENAI_API_KEY&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Agent wraps the browser, the LLM, and the duty loop collectively.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># max_actions_per_step limits what number of actions the agent takes earlier than<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># re-reading the web page &#8212; prevents runaway loops on advanced pages.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">agent<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">Agent<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">activity<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">activity<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">llm<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">llm<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">max_actions_per_step<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">5<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># run() executes the complete activity loop:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># learn web page \u2192 determine motion \u2192 take motion \u2192 learn up to date web page \u2192 repeat<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">outcome<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">agent<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">run<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># final_result() returns the agent&#8217;s extracted content material or conclusion<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">outcome<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">final_result<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">or<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;Process accomplished with no extracted output.&#8221;<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">foremost<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">activity<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Go to https:\/\/books.toscrape.com and discover the three costliest books &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;on the primary web page. Return their titles and costs.&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Process: {activity}n&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">output<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">run_browser_task<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">activity<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Outcome:n{output}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">asyncio<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">run<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-e\">foremost<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div><\/div><\/div>\n<p>What this does: Your entire activity, navigating to the positioning, studying the web page, figuring out the three highest costs, and extracting them, is dealt with by the agent with out a single CSS selector in your code. If books.toscrape.com redesigns its worth show tomorrow, the script nonetheless works. With a selector-based scraper, it will break silently.<\/p>\n<p>The max_actions_per_step=5 parameter is price explaining. On every step, the agent reads the web page and may determine to take as much as 5 actions (click on, sort, scroll, navigate) earlier than re-reading the web page. Maintaining this low forces the agent to examine its work extra regularly, which catches errors earlier.<\/p>\n<h2>Dealing with the Onerous Components<\/h2>\n<p>Three issues break most browser brokers in manufacturing. Every has an answer, however none of them is clear till you might have already been burned.<\/p>\n<p>1. Anti-Bot DetectionWebsites that don&#8217;t need to be automated detect automation in a number of methods, akin to checking the navigator.webdriver property (which Playwright units to true by default), searching for headless browser fingerprints within the JavaScript setting, and analyzing interplay patterns which might be too quick or too uniform to be human.<\/p>\n<p>Crucial mitigation is eradicating the webdriver flag. Past that, a sensible consumer agent string, a typical viewport dimension, and a sensible locale and timezone cowl most detection strategies wanting refined fingerprint evaluation.<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b225706381539892\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\n# hard_parts.py &#8212; Half 1: Anti-bot stealth launch&#13;<br \/>\n# Stipulations: pip set up playwright &amp;&amp; playwright set up chromium&#13;<br \/>\n# Learn how to run: python hard_parts.py&#13;<br \/>\n&#13;<br \/>\nimport asyncio&#13;<br \/>\nimport json&#13;<br \/>\nfrom pathlib import Path&#13;<br \/>\nfrom playwright.async_api import async_playwright&#13;<br \/>\n&#13;<br \/>\nasync def launch_stealth_browser(playwright):&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    Launch a browser context that appears extra like an actual human session.&#13;<br \/>\n    Covers: reasonable viewport, user-agent, locale, timezone, webdriver flag.&#13;<br \/>\n    Word: For severe anti-bot targets, think about a paid service like Browserbase.&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    browser = await playwright.chromium.launch(&#13;<br \/>\n        headless=True,&#13;<br \/>\n        args=[&#13;<br \/>\n            &#8220;&#8211;disable-blink-features=AutomationControlled&#8221;,  # Hides webdriver detection&#13;<br \/>\n            &#8220;&#8211;no-sandbox&#8221;,&#13;<br \/>\n            &#8220;&#8211;disable-dev-shm-usage&#8221;,&#13;<br \/>\n        ]&#13;<br \/>\n    )&#13;<br \/>\n&#13;<br \/>\n    context = await browser.new_context(&#13;<br \/>\n        viewport={&#8220;width&#8221;: 1366, &#8220;peak&#8221;: 768},   # Frequent desktop decision&#13;<br \/>\n        user_agent=(&#13;<br \/>\n            &#8220;Mozilla\/5.0 (Home windows NT 10.0; Win64; x64) &#8220;&#13;<br \/>\n            &#8220;AppleWebKit\/537.36 (KHTML, like Gecko) &#8220;&#13;<br \/>\n            &#8220;Chrome\/124.0.0.0 Safari\/537.36&#8243;&#13;<br \/>\n        ),&#13;<br \/>\n        locale=&#8221;en-US&#8221;,&#13;<br \/>\n        timezone_id=&#8221;America\/New_York&#8221;,&#13;<br \/>\n        java_script_enabled=True,&#13;<br \/>\n    )&#13;<br \/>\n&#13;<br \/>\n    # Take away the &#8216;webdriver&#8217; property that Playwright injects by default.&#13;<br \/>\n    # Bot detection programs examine for this within the browser&#8217;s JS setting.&#13;<br \/>\n    await context.add_init_script(&#13;<br \/>\n        &#8220;Object.defineProperty(navigator, &#8216;webdriver&#8217;, {get: () =&gt; undefined})&#8221;&#13;<br \/>\n    )&#13;<br \/>\n&#13;<br \/>\n    return browser, context<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<p>37<\/p>\n<p>38<\/p>\n<p>39<\/p>\n<p>40<\/p>\n<p>41<\/p>\n<p>42<\/p>\n<p>43<\/p>\n<\/div>\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># hard_parts.py &#8212; Half 1: Anti-bot stealth launch<\/span><\/p>\n<p><span class=\"crayon-p\"># Stipulations: pip set up playwright &amp;&amp; playwright set up chromium<\/span><\/p>\n<p><span class=\"crayon-p\"># Learn how to run: python hard_parts.py<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">asyncio<\/span><\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">json<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">pathlib <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">Path<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">playwright<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">async_api <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">async_playwright<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">launch_stealth_browser<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">playwright<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Launch a browser context that appears extra like an actual human session.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Covers: reasonable viewport, user-agent, locale, timezone, webdriver flag.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Word: For severe anti-bot targets, think about a paid service like Browserbase.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">playwright<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">chromium<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">launch<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">headless<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-t\">True<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">args<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">[<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8211;disable-blink-features=AutomationControlled&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\">\u00a0\u00a0<\/span><span class=\"crayon-p\"># Hides webdriver detection<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8211;no-sandbox&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8211;disable-dev-shm-usage&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">new_context<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">viewport<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;width&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">1366<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;peak&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">768<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\">\u00a0\u00a0 <\/span><span class=\"crayon-p\"># Frequent desktop decision<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">user_agent<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Mozilla\/5.0 (Home windows NT 10.0; Win64; x64) &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;AppleWebKit\/537.36 (KHTML, like Gecko) &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Chrome\/124.0.0.0 Safari\/537.36&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">locale<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;en-US&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">timezone_id<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;America\/New_York&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">java_script_enabled<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-t\">True<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Take away the &#8216;webdriver&#8217; property that Playwright injects by default.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Bot detection programs examine for this within the browser&#8217;s JS setting.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">add_init_script<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Object.defineProperty(navigator, &#8216;webdriver&#8217;, {get: () =&gt; undefined})&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">context<\/span><\/p>\n<\/div><\/div><\/div>\n<p>What this does: The add_init_script() name runs earlier than any web page JavaScript executes, which suggests the navigator.webdriver override is in place earlier than the positioning\u2019s detection code can examine for it. The \u2013disable-blink-features=AutomationControlled launch argument removes a separate automation flag on the browser engine stage. Collectively, these two adjustments deal with the commonest detection strategies.<\/p>\n<p>For websites with aggressive fingerprinting and CAPTCHA programs, these mitigations won&#8217;t be sufficient. Providers like Browserbase, Spidra and Brightdata\u2019s Scraping Browser deal with CAPTCHA fixing, residential IP rotation, and browser fingerprint administration as managed infrastructure.<\/p>\n<p>2. Sensible Ready<\/p>\n<p>The second failure mode is timing. The reflex is so as to add time.sleep() calls and enhance them when issues break. That is mistaken in each instructions: too brief on gradual connections, too lengthy on quick ones, and utterly opaque when debugging.<\/p>\n<p>Playwright has 4 correct wait methods. Use the one which matches what you&#8217;re truly ready for:<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b22570a020967592\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\n# Half 2: Sensible ready methods (add to your scraper or agent instruments)&#13;<br \/>\n&#13;<br \/>\nasync def smart_wait_examples(web page):&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    4 methods to attend for the suitable web page state, with out arbitrary sleeps.&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    # STRATEGY 1: Look forward to a particular component to seem within the DOM&#13;<br \/>\n    # Use when you understand precisely what component alerts content material has loaded&#13;<br \/>\n    await web page.wait_for_selector(&#8220;.product-list&#8221;, state=&#8221;seen&#8221;, timeout=10000)&#13;<br \/>\n&#13;<br \/>\n    # STRATEGY 2: Look forward to a particular API response&#13;<br \/>\n    # Use when the content material comes from an XHR\/fetch name you possibly can determine&#13;<br \/>\n    async with web page.expect_response(&#13;<br \/>\n        lambda r: &#8220;\/api\/merchandise&#8221; in r.url and r.standing == 200&#13;<br \/>\n    ) as response_info:&#13;<br \/>\n        await web page.click on(&#8220;#load-more&#8221;)&#13;<br \/>\n    response = await response_info.worth&#13;<br \/>\n    print(f&#8221;API responded: {response.standing}&#8221;)&#13;<br \/>\n&#13;<br \/>\n    # STRATEGY 3: Look forward to the URL to vary after type submission&#13;<br \/>\n    # Use when a profitable submit redirects to a brand new web page&#13;<br \/>\n    await web page.wait_for_url(&#8220;**\/dashboard**&#8221;, timeout=10000)&#13;<br \/>\n&#13;<br \/>\n    # STRATEGY 4: Look forward to a JavaScript variable to be set&#13;<br \/>\n    # Use when no visible component reliably alerts the prepared state&#13;<br \/>\n    await web page.wait_for_function(&#13;<br \/>\n        &#8220;() =&gt; window.__dataLoaded === true&#8221;,&#13;<br \/>\n        timeout=10000&#13;<br \/>\n    )<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<\/div>\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># Half 2: Sensible ready methods (add to your scraper or agent instruments)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">smart_wait_examples<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a04 methods to attend for the suitable web page state, with out arbitrary sleeps.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># STRATEGY 1: Look forward to a particular component to seem within the DOM<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Use when you understand precisely what component alerts content material has loaded<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">wait_for_selector<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;.product-list&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">state<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;seen&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">timeout<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">10000<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># STRATEGY 2: Look forward to a particular API response<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Use when the content material comes from an XHR\/fetch name you possibly can determine<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">with <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">expect_response<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-i\">lambda<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">r<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;\/api\/merchandise&#8221;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">r<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">url <\/span><span class=\"crayon-st\">and<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">r<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">standing<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">==<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">200<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">as<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">response_info<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">click on<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;#load-more&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">response<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">response_info<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">worth<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;API responded: {response.standing}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># STRATEGY 3: Look forward to the URL to vary after type submission<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Use when a profitable submit redirects to a brand new web page<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">wait_for_url<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;**\/dashboard**&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">timeout<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">10000<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># STRATEGY 4: Look forward to a JavaScript variable to be set<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Use when no visible component reliably alerts the prepared state<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">wait_for_function<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;() =&gt; window.__dataLoaded === true&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">timeout<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">10000<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div><\/div><\/div>\n<p>What this does: Every technique is tied to a particular observable occasion relatively than an arbitrary time delay. wait_for_selector watches the DOM. expect_response hooks into the community layer. wait_for_url screens navigation. wait_for_function evaluates JavaScript within the browser context. Use whichever one most instantly alerts \u201cthe factor I want is now prepared.\u201d<\/p>\n<p>3. Session and Cookie PersistenceThe third failure mode is shedding session state. In case your agent logs right into a website throughout the 1st step after which the browser context is destroyed, step two has no authentication. Recreating the login on each run is gradual and may set off fee limiting or lockout.<\/p>\n<p>The answer is saving cookies to disk after login and loading them at the beginning of each subsequent run:<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b225712892584482\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\n# Half 3: Session persistence throughout runs&#13;<br \/>\n&#13;<br \/>\nCOOKIES_FILE = Path(&#8220;session_cookies.json&#8221;)&#13;<br \/>\n&#13;<br \/>\nasync def save_session(context) -&gt; None:&#13;<br \/>\n    &#8220;&#8221;&#8221;Save browser cookies to disk after a profitable login.&#8221;&#8221;&#8221;&#13;<br \/>\n    cookies = await context.cookies()&#13;<br \/>\n    COOKIES_FILE.write_text(json.dumps(cookies, indent=2))&#13;<br \/>\n    print(f&#8221;Session saved: {len(cookies)} cookies written.&#8221;)&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\nasync def load_session(context) -&gt; bool:&#13;<br \/>\n    &#8220;&#8221;&#8221;Load saved cookies earlier than navigating. Returns True if session was discovered.&#8221;&#8221;&#8221;&#13;<br \/>\n    if not COOKIES_FILE.exists():&#13;<br \/>\n        print(&#8220;No saved session. Contemporary login required.&#8221;)&#13;<br \/>\n        return False&#13;<br \/>\n    cookies = json.masses(COOKIES_FILE.read_text())&#13;<br \/>\n    await context.add_cookies(cookies)&#13;<br \/>\n    print(f&#8221;Session restored: {len(cookies)} cookies loaded.&#8221;)&#13;<br \/>\n    return True<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<\/div>\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># Half 3: Session persistence throughout runs<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">COOKIES_FILE<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">Path<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;session_cookies.json&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">save_session<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">None<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;Save browser cookies to disk after a profitable login.&#8221;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">cookies<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">cookies<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">COOKIES_FILE<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">write_text<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">json<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">dumps<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">cookies<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">indent<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">2<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Session saved: {len(cookies)} cookies written.&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">load_session<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-t\">bool<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;Load saved cookies earlier than navigating. Returns True if session was discovered.&#8221;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">not<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">COOKIES_FILE<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">exists<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;No saved session. Contemporary login required.&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-t\">False<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">cookies<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">json<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">masses<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">COOKIES_FILE<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">read_text<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">add_cookies<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">cookies<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Session restored: {len(cookies)} cookies loaded.&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-t\">True<\/span><\/p>\n<\/div><\/div><\/div>\n<p>What this does: context.cookies() returns all cookies for the present browser context, together with session tokens and authentication cookies. Writing them to JSON and reloading them on the subsequent run means the browser begins in an authenticated state. Word that periods expire; add a examine that falls again to a recent login if the saved session returns a redirect to the login web page.<\/p>\n<h2>Deploying Browser Brokers<\/h2>\n<p>Getting a browser agent working domestically is one factor. Working it reliably in a cloud setting is one other.<\/p>\n<p>The primary distinction between a Python script that works in your laptop computer and one which fails in CI is system dependencies. Playwright\u2019s Chromium browser requires a set of shared libraries which might be current on most developer machines however absent from minimal cloud pictures. The cleanest answer is Docker.<\/p>\n<p>Dockerfile \u2014 construct a container that ships every part Playwright wants:<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b22571a798386688\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\n# Dockerfile for headless Playwright-based browser agent&#13;<br \/>\n# Construct: docker construct -t browser-agent .&#13;<br \/>\n# Run:   docker run &#8211;rm -e OPENAI_API_KEY=your_key browser-agent&#13;<br \/>\n&#13;<br \/>\nFROM python:3.11-slim&#13;<br \/>\n&#13;<br \/>\n# Set up system dependencies required by Chromium&#13;<br \/>\nRUN apt-get replace &amp;&amp; apt-get set up -y &#13;<br \/>\n    libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 &#13;<br \/>\n    libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 &#13;<br \/>\n    libxrandr2 libgbm1 libasound2 libpangocairo-1.0-0 &#13;<br \/>\n    libpango-1.0-0 libcairo2 libx11-6 libxext6 libxfixes3 &#13;<br \/>\n    fonts-liberation wget ca-certificates &#13;<br \/>\n    &amp;&amp; rm -rf \/var\/lib\/apt\/lists\/*&#13;<br \/>\n&#13;<br \/>\nWORKDIR \/app&#13;<br \/>\n&#13;<br \/>\n# Set up Python dependencies first (cached layer &#8212; solely rebuilds on necessities change)&#13;<br \/>\nCOPY necessities.txt .&#13;<br \/>\nRUN pip set up &#8211;no-cache-dir -r necessities.txt&#13;<br \/>\n&#13;<br \/>\n# Set up Playwright browser binaries into the picture&#13;<br \/>\nRUN playwright set up chromium&#13;<br \/>\nRUN playwright install-deps chromium&#13;<br \/>\n&#13;<br \/>\n# Copy software code final (adjustments right here do not invalidate the pip\/playwright layers)&#13;<br \/>\nCOPY . .&#13;<br \/>\n&#13;<br \/>\nCMD [&#8220;python&#8221;, &#8220;agent_tools.py&#8221;]&#13;<br \/>\n&#13;<br \/>\nnecessities.txt:&#13;<br \/>\nplaywright&#13;<br \/>\nbrowser-use&#13;<br \/>\nlangchain&#13;<br \/>\nlangchain-openai&#13;<br \/>\nlanggraph&#13;<br \/>\npython-dotenv<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<p>37<\/p>\n<\/div>\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># Dockerfile for headless Playwright-based browser agent<\/span><\/p>\n<p><span class=\"crayon-p\"># Construct: docker construct -t browser-agent .<\/span><\/p>\n<p><span class=\"crayon-p\"># Run:\u00a0\u00a0 docker run &#8211;rm -e OPENAI_API_KEY=your_key browser-agent<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">FROM <\/span><span class=\"crayon-v\">python<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-cn\">3.11<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-i\">slim<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Set up system dependencies required by Chromium<\/span><\/p>\n<p><span class=\"crayon-e\">RUN <\/span><span class=\"crayon-v\">apt<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">get <\/span><span class=\"crayon-v\">replace<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">&amp;&amp;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">apt<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">get <\/span><span class=\"crayon-v\">set up<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-i\">y<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\"><\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">libnss3 <\/span><span class=\"crayon-v\">libatk1<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">libatk<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">bridge2<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">libcups2<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\"><\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">libdrm2 <\/span><span class=\"crayon-e\">libxkbcommon0 <\/span><span class=\"crayon-e\">libxcomposite1 <\/span><span class=\"crayon-i\">libxdamage1<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\"><\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">libxrandr2 <\/span><span class=\"crayon-e\">libgbm1 <\/span><span class=\"crayon-e\">libasound2 <\/span><span class=\"crayon-v\">libpangocairo<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-cn\">1.0<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\"><\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">libpango<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-cn\">1.0<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">libcairo2 <\/span><span class=\"crayon-v\">libx11<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-cn\">6<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">libxext6 <\/span><span class=\"crayon-i\">libxfixes3<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\"><\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">fonts<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">liberation <\/span><span class=\"crayon-e\">wget <\/span><span class=\"crayon-v\">ca<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-i\">certificates<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\"><\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-o\">&amp;&amp;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">rm<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">rf<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">\/<\/span><span class=\"crayon-t\">var<\/span><span class=\"crayon-o\">\/<\/span><span class=\"crayon-v\">lib<\/span><span class=\"crayon-o\">\/<\/span><span class=\"crayon-v\">apt<\/span><span class=\"crayon-o\">\/<\/span><span class=\"crayon-v\">lists<\/span><span class=\"crayon-o\">\/<\/span><span class=\"crayon-o\">*<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">WORKDIR<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">\/<\/span><span class=\"crayon-i\">app<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Set up Python dependencies first (cached layer &#8212; solely rebuilds on necessities change)<\/span><\/p>\n<p><span class=\"crayon-e\">COPY <\/span><span class=\"crayon-v\">necessities<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-i\">txt<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">.<\/span><\/p>\n<p><span class=\"crayon-e\">RUN <\/span><span class=\"crayon-e\">pip <\/span><span class=\"crayon-v\">set up<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">&#8212;<\/span><span class=\"crayon-v\">no<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">cache<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">dir<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-i\">r<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">necessities<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-i\">txt<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Set up Playwright browser binaries into the picture<\/span><\/p>\n<p><span class=\"crayon-e\">RUN <\/span><span class=\"crayon-e\">playwright <\/span><span class=\"crayon-e\">set up <\/span><span class=\"crayon-e\">chromium<\/span><\/p>\n<p><span class=\"crayon-e\">RUN <\/span><span class=\"crayon-e\">playwright <\/span><span class=\"crayon-v\">set up<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">deps <\/span><span class=\"crayon-i\">chromium<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Copy software code final (adjustments right here do not invalidate the pip\/playwright layers)<\/span><\/p>\n<p><span class=\"crayon-i\">COPY<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">.<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-i\">CMD<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-s\">&#8220;python&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;agent_tools.py&#8221;<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">necessities<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">txt<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-e\">playwright<\/span><\/p>\n<p><span class=\"crayon-v\">browser<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-st\">use<\/span><\/p>\n<p><span class=\"crayon-e\">langchain<\/span><\/p>\n<p><span class=\"crayon-v\">langchain<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">openai<\/span><\/p>\n<p><span class=\"crayon-e\">langgraph<\/span><\/p>\n<p><span class=\"crayon-v\">python<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">dotenv<\/span><\/p>\n<\/div><\/div><\/div>\n<p>For concurrent workloads operating a number of browser periods in parallel, use Playwright\u2019s async API with asyncio.collect():<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b22571e324601082\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\n# Parallel scraping with semaphore fee limiting&#13;<br \/>\n# Runs as much as 3 browser periods concurrently&#13;<br \/>\n&#13;<br \/>\nimport asyncio&#13;<br \/>\nfrom playwright.async_api import async_playwright&#13;<br \/>\n&#13;<br \/>\nasync def scrape_url(browser, url: str, semaphore: asyncio.Semaphore) -&gt; dict:&#13;<br \/>\n    &#8220;&#8221;&#8221;Scrape a single URL, respecting the concurrency semaphore.&#8221;&#8221;&#8221;&#13;<br \/>\n    async with semaphore:&#13;<br \/>\n        context = await browser.new_context()&#13;<br \/>\n        web page = await context.new_page()&#13;<br \/>\n        await web page.goto(url, wait_until=&#8221;domcontentloaded&#8221;)&#13;<br \/>\n        title = await web page.title()&#13;<br \/>\n        await context.shut()   # Shut context (not browser) to launch assets&#13;<br \/>\n        return {&#8220;url&#8221;: url, &#8220;title&#8221;: title}&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\nasync def scrape_parallel(urls: record[str], max_concurrent: int = 3) -&gt; record[dict]:&#13;<br \/>\n    &#8220;&#8221;&#8221;Scrape an inventory of URLs in parallel, capped at max_concurrent periods.&#8221;&#8221;&#8221;&#13;<br \/>\n    semaphore = asyncio.Semaphore(max_concurrent)  # Cap concurrent periods&#13;<br \/>\n&#13;<br \/>\n    async with async_playwright() as p:&#13;<br \/>\n        # One browser shared throughout all contexts &#8212; less expensive than one browser per URL&#13;<br \/>\n        browser = await p.chromium.launch(headless=True)&#13;<br \/>\n        duties = [scrape_url(browser, url, semaphore) for url in urls]&#13;<br \/>\n        outcomes = await asyncio.collect(*duties)&#13;<br \/>\n        await browser.shut()&#13;<br \/>\n&#13;<br \/>\n    return record(outcomes)<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<\/div>\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># Parallel scraping with semaphore fee limiting<\/span><\/p>\n<p><span class=\"crayon-p\"># Runs as much as 3 browser periods concurrently<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">asyncio<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">playwright<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">async_api <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">async_playwright<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">scrape_url<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">url<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">semaphore<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">asyncio<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">Semaphore<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">dict<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;Scrape a single URL, respecting the concurrency semaphore.&#8221;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">with <\/span><span class=\"crayon-v\">semaphore<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">new_context<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">new_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-st\">goto<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">url<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">wait_until<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;domcontentloaded&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">title<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">title<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">shut<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\">\u00a0\u00a0 <\/span><span class=\"crayon-p\"># Shut context (not browser) to launch assets<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;url&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">url<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;title&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">title<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">scrape_parallel<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">urls<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">record<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">max_concurrent<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-t\">int<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">3<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">record<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-v\">dict<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;Scrape an inventory of URLs in parallel, capped at max_concurrent periods.&#8221;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">semaphore<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">asyncio<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">Semaphore<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">max_concurrent<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\">\u00a0\u00a0<\/span><span class=\"crayon-p\"># Cap concurrent periods<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">with <\/span><span class=\"crayon-e\">async_playwright<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">as<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">p<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># One browser shared throughout all contexts &#8212; less expensive than one browser per URL<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">await<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">p<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">chromium<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">launch<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">headless<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-t\">True<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">duties<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-e\">scrape_url<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">url<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">semaphore<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">url <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">urls<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">outcomes<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">asyncio<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">collect<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-o\">*<\/span><span class=\"crayon-v\">duties<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">browser<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">shut<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">record<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">outcomes<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div><\/div><\/div>\n<p>What this does: The asyncio.Semaphore(max_concurrent) caps what number of browser contexts run on the identical time. With out it, launching 50 concurrent browser contexts will exhaust reminiscence. One browser course of is shared throughout all contexts; a context is affordable; a full browser occasion shouldn&#8217;t be.<\/p>\n<p>On the managed infrastructure facet, Amazon Nova Act launched in March 2025 as a devoted SDK for constructing browser brokers on AWS, integrating natively with Playwright for browser management. Playwright\u2019s personal MCP server provides AI assistants full browser management by means of the Mannequin Context Protocol, utilizing structured accessibility snapshots relatively than screenshots, which suggests token prices keep low whereas the agent\u2019s understanding of the web page stays excessive.<\/p>\n<h2>Placing It All Collectively<\/h2>\n<p>Here&#8217;s a full end-to-end agent that takes a analysis query, navigates to a public information supply, extracts structured outcomes, and returns a clear abstract. It makes use of the browser instruments from Part 5 orchestrated by a LangGraph agent.<\/p>\n<p>Learn how to run: Save as reference_agent.py, guarantee OPENAI_API_KEY is in your .env, and run python reference_agent.py<\/p>\n<div id=\"urvanov-syntax-highlighter-6a3931b225723330652853\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p>\n# reference_agent.py&#13;<br \/>\n# Full browser-using AI agent: navigates, extracts, summarizes.&#13;<br \/>\n# Goal: books.toscrape.com (public scraping sandbox)&#13;<br \/>\n# Stipulations: pip set up playwright langchain langchain-openai langgraph python-dotenv&#13;<br \/>\n#                playwright set up chromium&#13;<br \/>\n# Learn how to run: python reference_agent.py&#13;<br \/>\n&#13;<br \/>\nimport asyncio&#13;<br \/>\nimport os&#13;<br \/>\nfrom dotenv import load_dotenv&#13;<br \/>\nfrom langchain_openai import ChatOpenAI&#13;<br \/>\nfrom langchain.instruments import instrument&#13;<br \/>\nfrom langchain_core.messages import HumanMessage, SystemMessage&#13;<br \/>\nfrom langgraph.prebuilt import create_react_agent&#13;<br \/>\nfrom playwright.async_api import async_playwright&#13;<br \/>\n&#13;<br \/>\nload_dotenv()&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 BROWSER STATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n_browser = None&#13;<br \/>\n_context = None&#13;<br \/>\n_page = None&#13;<br \/>\n_playwright = None&#13;<br \/>\n&#13;<br \/>\nasync def get_page():&#13;<br \/>\n    international _browser, _context, _page, _playwright&#13;<br \/>\n    if _browser is None:&#13;<br \/>\n        _playwright = await async_playwright().begin()&#13;<br \/>\n        _browser = await _playwright.chromium.launch(headless=True)&#13;<br \/>\n        _context = await _browser.new_context(&#13;<br \/>\n            viewport={&#8220;width&#8221;: 1280, &#8220;peak&#8221;: 720},&#13;<br \/>\n            user_agent=(&#13;<br \/>\n                &#8220;Mozilla\/5.0 (Home windows NT 10.0; Win64; x64) &#8220;&#13;<br \/>\n                &#8220;AppleWebKit\/537.36 (KHTML, like Gecko) &#8220;&#13;<br \/>\n                &#8220;Chrome\/120.0.0.0 Safari\/537.36&#8243;&#13;<br \/>\n            )&#13;<br \/>\n        )&#13;<br \/>\n        # Take away webdriver fingerprint&#13;<br \/>\n        await _context.add_init_script(&#13;<br \/>\n            &#8220;Object.defineProperty(navigator, &#8216;webdriver&#8217;, {get: () =&gt; undefined})&#8221;&#13;<br \/>\n        )&#13;<br \/>\n        _page = await _context.new_page()&#13;<br \/>\n    return _page&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\nasync def teardown():&#13;<br \/>\n    international _browser, _playwright&#13;<br \/>\n    if _browser:&#13;<br \/>\n        await _browser.shut()&#13;<br \/>\n        await _playwright.cease()&#13;<br \/>\n        _browser = None&#13;<br \/>\n        _playwright = None&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 TOOLS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n&#13;<br \/>\n@instrument&#13;<br \/>\nasync def navigate(url: str) -&gt; str:&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    Navigate the browser to a URL and return the web page&#8217;s textual content content material.&#13;<br \/>\n    Use when it&#8217;s essential to open an internet site or transfer to a brand new web page.&#13;<br \/>\n    Enter: full URL with https:\/\/ prefix.&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    web page = await get_page()&#13;<br \/>\n    await web page.goto(url, wait_until=&#8221;domcontentloaded&#8221;, timeout=20000)&#13;<br \/>\n    await web page.wait_for_load_state(&#8220;networkidle&#8221;)&#13;<br \/>\n    content material = await web page.inner_text(&#8220;physique&#8221;)&#13;<br \/>\n    return content material[:4000]&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n@instrument&#13;<br \/>\nasync def extract_structured(css_selector: str) -&gt; str:&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    Extract textual content from all components matching a CSS selector on the present web page.&#13;<br \/>\n    Use when it&#8217;s essential to pull particular components from the loaded web page.&#13;<br \/>\n    Enter: legitimate CSS selector string (e.g., &#8216;h3 a&#8217;, &#8216;.price_color&#8217;, &#8216;article.product_pod&#8217;).&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    web page = await get_page()&#13;<br \/>\n    strive:&#13;<br \/>\n        await web page.wait_for_selector(css_selector, timeout=5000)&#13;<br \/>\n        components = await web page.query_selector_all(css_selector)&#13;<br \/>\n        texts = []&#13;<br \/>\n        for el in components[:20]:  # Cap at 20 components to maintain output manageable&#13;<br \/>\n            textual content = await el.inner_text()&#13;<br \/>\n            texts.append(textual content.strip())&#13;<br \/>\n        return &#8220;n&#8221;.be a part of(texts) if texts else &#8220;No components discovered.&#8221;&#13;<br \/>\n    besides Exception as e:&#13;<br \/>\n        return f&#8221;Extraction failed: {str(e)}&#8221;&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n@instrument&#13;<br \/>\nasync def get_current_url() -&gt; str:&#13;<br \/>\n    &#8220;&#8221;&#8221;Return the URL the browser is at the moment on. No enter required.&#8221;&#8221;&#8221;&#13;<br \/>\n    web page = await get_page()&#13;<br \/>\n    return web page.url&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 AGENT \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n&#13;<br \/>\nllm = ChatOpenAI(&#13;<br \/>\n    mannequin=&#8221;gpt-4o&#8221;,&#13;<br \/>\n    temperature=0,&#13;<br \/>\n    api_key=os.getenv(&#8220;OPENAI_API_KEY&#8221;)&#13;<br \/>\n)&#13;<br \/>\n&#13;<br \/>\ninstruments = [navigate, extract_structured, get_current_url]&#13;<br \/>\nagent = create_react_agent(llm, instruments)&#13;<br \/>\n&#13;<br \/>\nSYSTEM = (&#13;<br \/>\n    &#8220;You&#8217;re a browser-based analysis agent. You might have entry to an actual browser. &#8220;&#13;<br \/>\n    &#8220;Use navigate() to open pages, extract_structured() to tug particular components, &#8220;&#13;<br \/>\n    &#8220;and get_current_url() to examine the place you&#8217;re. &#8220;&#13;<br \/>\n    &#8220;All the time navigate first, then extract. Be concise in your closing reply.&#8221;&#13;<br \/>\n)&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\nasync def run_agent(question: str) -&gt; str:&#13;<br \/>\n    outcome = await agent.ainvoke({&#13;<br \/>\n        &#8220;messages&#8221;: [&#13;<br \/>\n            SystemMessage(content=SYSTEM),&#13;<br \/>\n            HumanMessage(content=query)&#13;<br \/>\n        ]&#13;<br \/>\n    })&#13;<br \/>\n    await teardown()&#13;<br \/>\n    return outcome[&#8220;messages&#8221;][-1].content material&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 DEMO \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n&#13;<br \/>\nif __name__ == &#8220;__main__&#8221;:&#13;<br \/>\n    question = (&#13;<br \/>\n        &#8220;Go to https:\/\/books.toscrape.com and extract the titles and costs &#8220;&#13;<br \/>\n        &#8220;of the primary 5 books listed. Return them as a structured record.&#8221;&#13;<br \/>\n    )&#13;<br \/>\n    print(f&#8221;Question: {question}n&#8221;)&#13;<br \/>\n    reply = asyncio.run(run_agent(question))&#13;<br \/>\n    print(f&#8221;Reply:n{reply}&#8221;)<\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<p>37<\/p>\n<p>38<\/p>\n<p>39<\/p>\n<p>40<\/p>\n<p>41<\/p>\n<p>42<\/p>\n<p>43<\/p>\n<p>44<\/p>\n<p>45<\/p>\n<p>46<\/p>\n<p>47<\/p>\n<p>48<\/p>\n<p>49<\/p>\n<p>50<\/p>\n<p>51<\/p>\n<p>52<\/p>\n<p>53<\/p>\n<p>54<\/p>\n<p>55<\/p>\n<p>56<\/p>\n<p>57<\/p>\n<p>58<\/p>\n<p>59<\/p>\n<p>60<\/p>\n<p>61<\/p>\n<p>62<\/p>\n<p>63<\/p>\n<p>64<\/p>\n<p>65<\/p>\n<p>66<\/p>\n<p>67<\/p>\n<p>68<\/p>\n<p>69<\/p>\n<p>70<\/p>\n<p>71<\/p>\n<p>72<\/p>\n<p>73<\/p>\n<p>74<\/p>\n<p>75<\/p>\n<p>76<\/p>\n<p>77<\/p>\n<p>78<\/p>\n<p>79<\/p>\n<p>80<\/p>\n<p>81<\/p>\n<p>82<\/p>\n<p>83<\/p>\n<p>84<\/p>\n<p>85<\/p>\n<p>86<\/p>\n<p>87<\/p>\n<p>88<\/p>\n<p>89<\/p>\n<p>90<\/p>\n<p>91<\/p>\n<p>92<\/p>\n<p>93<\/p>\n<p>94<\/p>\n<p>95<\/p>\n<p>96<\/p>\n<p>97<\/p>\n<p>98<\/p>\n<p>99<\/p>\n<p>100<\/p>\n<p>101<\/p>\n<p>102<\/p>\n<p>103<\/p>\n<p>104<\/p>\n<p>105<\/p>\n<p>106<\/p>\n<p>107<\/p>\n<p>108<\/p>\n<p>109<\/p>\n<p>110<\/p>\n<p>111<\/p>\n<p>112<\/p>\n<p>113<\/p>\n<p>114<\/p>\n<p>115<\/p>\n<p>116<\/p>\n<p>117<\/p>\n<p>118<\/p>\n<p>119<\/p>\n<p>120<\/p>\n<p>121<\/p>\n<p>122<\/p>\n<p>123<\/p>\n<p>124<\/p>\n<p>125<\/p>\n<p>126<\/p>\n<p>127<\/p>\n<p>128<\/p>\n<p>129<\/p>\n<p>130<\/p>\n<p>131<\/p>\n<p>132<\/p>\n<p>133<\/p>\n<p>134<\/p>\n<p>135<\/p>\n<p>136<\/p>\n<p>137<\/p>\n<\/div>\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># reference_agent.py<\/span><\/p>\n<p><span class=\"crayon-p\"># Full browser-using AI agent: navigates, extracts, summarizes.<\/span><\/p>\n<p><span class=\"crayon-p\"># Goal: books.toscrape.com (public scraping sandbox)<\/span><\/p>\n<p><span class=\"crayon-p\"># Stipulations: pip set up playwright langchain langchain-openai langgraph python-dotenv<\/span><\/p>\n<p><span class=\"crayon-p\">#\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0playwright set up chromium<\/span><\/p>\n<p><span class=\"crayon-p\"># Learn how to run: python reference_agent.py<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">asyncio<\/span><\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">os<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">dotenv <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">load_dotenv<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">langchain_openai <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">ChatOpenAI<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langchain<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">instruments <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">instrument<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langchain_core<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">messages <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-v\">HumanMessage<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">SystemMessage<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langgraph<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">prebuilt <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">create_react_agent<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">playwright<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">async_api <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">async_playwright<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">load_dotenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 BROWSER STATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">None<\/span><\/p>\n<p><span class=\"crayon-v\">_context<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">None<\/span><\/p>\n<p><span class=\"crayon-v\">_page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">None<\/span><\/p>\n<p><span class=\"crayon-v\">_playwright<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">None<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">get_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-m\">international<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">_context<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">_page<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">_playwright<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">_browser <\/span><span class=\"crayon-st\">is<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">None<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">_playwright<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">async_playwright<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">begin<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">_playwright<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">chromium<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">launch<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">headless<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-t\">True<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">_context<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">new_context<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">viewport<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;width&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">1280<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;peak&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">720<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">user_agent<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Mozilla\/5.0 (Home windows NT 10.0; Win64; x64) &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;AppleWebKit\/537.36 (KHTML, like Gecko) &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Chrome\/120.0.0.0 Safari\/537.36&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Take away webdriver fingerprint<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">_context<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">add_init_script<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Object.defineProperty(navigator, &#8216;webdriver&#8217;, {get: () =&gt; undefined})&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">_page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">_context<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">new_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">_page<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">teardown<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-m\">international<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">_playwright<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">shut<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">_playwright<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">cease<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">_browser<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">None<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">_playwright<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">None<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 TOOLS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-sy\">@<\/span><span class=\"crayon-e\">instrument<\/span><\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">navigate<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">url<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Navigate the browser to a URL and return the web page&#8217;s textual content content material.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Use when it&#8217;s essential to open an internet site or transfer to a brand new web page.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Enter: full URL with https:\/\/ prefix.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">get_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-st\">goto<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">url<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">wait_until<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;domcontentloaded&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">timeout<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">20000<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">wait_for_load_state<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;networkidle&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">content material<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">inner_text<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;physique&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">content material<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-cn\">4000<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-sy\">@<\/span><span class=\"crayon-e\">instrument<\/span><\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">extract_structured<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">css_selector<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Extract textual content from all components matching a CSS selector on the present web page.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Use when it&#8217;s essential to pull particular components from the loaded web page.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Enter: legitimate CSS selector string (e.g., &#8216;h3 a&#8217;, &#8216;.price_color&#8217;, &#8216;article.product_pod&#8217;).<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">get_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">strive<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">wait_for_selector<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">css_selector<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">timeout<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">5000<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">components<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">query_selector_all<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">css_selector<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">texts<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">el <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">components<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-cn\">20<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\">\u00a0\u00a0<\/span><span class=\"crayon-p\"># Cap at 20 components to maintain output manageable<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">textual content<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">el<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">inner_text<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">texts<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">append<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">textual content<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">strip<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;n&#8221;<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">be a part of<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">texts<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">texts <\/span><span class=\"crayon-st\">else<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;No components discovered.&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">besides <\/span><span class=\"crayon-e\">Exception <\/span><span class=\"crayon-st\">as<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">e<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Extraction failed: {str(e)}&#8221;<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-sy\">@<\/span><span class=\"crayon-e\">instrument<\/span><\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">get_current_url<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;Return the URL the browser is at the moment on. No enter required.&#8221;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">get_page<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">web page<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-i\">url<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 AGENT \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">llm<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">ChatOpenAI<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">mannequin<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;gpt-4o&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">temperature<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">api_key<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">os<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">getenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;OPENAI_API_KEY&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">instruments<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-v\">navigate<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">extract_structured<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">get_current_url<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p><span class=\"crayon-v\">agent<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">create_react_agent<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">llm<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">instruments<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">SYSTEM<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;You&#8217;re a browser-based analysis agent. You might have entry to an actual browser. &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Use navigate() to open pages, extract_structured() to tug particular components, &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;and get_current_url() to examine the place you&#8217;re. &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;All the time navigate first, then extract. Be concise in your closing reply.&#8221;<\/span><\/p>\n<p><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">async <\/span><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">run_agent<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">question<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">outcome<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-v\">agent<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">ainvoke<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;messages&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">SystemMessage<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">content<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">SYSTEM<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">HumanMessage<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">content<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">query<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">await <\/span><span class=\"crayon-e\">teardown<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">outcome<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-s\">&#8220;messages&#8221;<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-cn\">1<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-i\">content material<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 DEMO \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">__name__<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">==<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;__main__&#8221;<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">question<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Go to https:\/\/books.toscrape.com and extract the titles and costs &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;of the primary 5 books listed. Return them as a structured record.&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Question: {question}n&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">reply<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">asyncio<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">run<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-e\">run_agent<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">question<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Reply:n{reply}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div><\/div><\/div>\n<p>What this does: This agent has three clear instruments: navigate, extract_structured, and get_current_url, plus a system immediate that tells it precisely when to make use of each. The agent calls navigate to load the web page, extract_structured to tug the ebook titles and costs by CSS selector, and synthesizes a structured record within the closing reply. The teardown() name after the agent finishes closes the browser cleanly so no zombie Chromium processes are left operating.<\/p>\n<h2>Conclusion<\/h2>\n<p>The browser shouldn&#8217;t be a specialised instrument for automation engineers. It&#8217;s the common interface for the online, and the online is the place a lot of the world\u2019s precise work will get achieved. An AI agent that may use a browser doesn&#8217;t want a companion staff sustaining API integrations. It will possibly attain something a human can attain.<\/p>\n<p>What makes this sensible now, not simply theoretically fascinating, is the maturity of the tooling. Playwright handles the laborious components of browser interplay. browser-use removes the necessity to write selectors for exploratory duties. LangGraph provides the LLM clear instrument hooks and a reasoning loop that handles variable web page buildings. The patterns on this article will not be demos. They&#8217;re the identical patterns 51% of enterprises now operating AI brokers in manufacturing are constructing on.<\/p>\n<p>Begin with the scraping instance. Get it operating towards a website you really need information from. Add the agent layer if you want selections the script can&#8217;t anticipate. Add browser-use when the web page construction is simply too dynamic for selectors. Deploy in Docker if you want it operating someplace aside from your laptop computer.<\/p>\n<p>The laborious half shouldn&#8217;t be the code. It&#8217;s realizing which instrument to achieve for at every layer. Hopefully this text made that clearer.<\/p>\n<\/p><\/div>\n<p><br \/>\n<br \/><a href=\"https:\/\/machinelearningmastery.com\/building-browser-using-ai-agents-in-python\/\">Source link <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>On this article, you&#8217;ll discover ways to construct AI brokers that may browse and work together with actual web sites utilizing Playwright, browser-use, and LangGraph. Matters we are going to cowl embody: Why Playwright is the suitable basis for browser automation in 2026, and the way it differs from Selenium. Learn how to scrape dynamic, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1340,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"fifu_image_url":"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2026\/06\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png","fifu_image_alt":"","jnews-multi-image_gallery":[],"jnews_single_post":[],"jnews_primary_category":[],"jnews_override_bookmark_settings":[],"jnews_social_meta":[],"jnews_override_counter":[],"footnotes":""},"categories":[7],"tags":[210,1774,920,219],"class_list":["post-1338","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-data-science-mlops","tag-agents","tag-browserusing","tag-building","tag-python"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Constructing Browser-Utilizing AI Brokers in Python - Future News 24<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Constructing Browser-Utilizing AI Brokers in Python - Future News 24\" \/>\n<meta property=\"og:description\" content=\"On this article, you&#8217;ll discover ways to construct AI brokers that may browse and work together with actual web sites utilizing Playwright, browser-use, and LangGraph. Matters we are going to cowl embody: Why Playwright is the suitable basis for browser automation in 2026, and the way it differs from Selenium. Learn how to scrape dynamic, [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/\" \/>\n<meta property=\"og:site_name\" content=\"Future News 24\" \/>\n<meta property=\"article:published_time\" content=\"2026-06-22T12:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-06-22T12:59:35+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2026\/06\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png\" \/><meta property=\"og:image\" content=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2026\/06\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png\" \/>\n<meta name=\"author\" content=\"Future News 24\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2026\/06\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Future News 24\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"48 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/2026\\\/06\\\/22\\\/building-browser-using-ai-agents-in-python\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/2026\\\/06\\\/22\\\/building-browser-using-ai-agents-in-python\\\/\"},\"author\":{\"name\":\"Future News 24\",\"@id\":\"https:\\\/\\\/futurenews24.com\\\/#\\\/schema\\\/person\\\/cecad1bde21cfc357cf70128144d6c83\"},\"headline\":\"Constructing Browser-Utilizing AI Brokers in Python\",\"datePublished\":\"2026-06-22T12:00:00+00:00\",\"dateModified\":\"2026-06-22T12:59:35+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/2026\\\/06\\\/22\\\/building-browser-using-ai-agents-in-python\\\/\"},\"wordCount\":9784,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/futurenews24.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/2026\\\/06\\\/22\\\/building-browser-using-ai-agents-in-python\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/machinelearningmastery.com\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png\",\"keywords\":[\"Agents\",\"BrowserUsing\",\"Building\",\"Python\"],\"articleSection\":[\"Data Science &amp; MLOps\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/2026\\\/06\\\/22\\\/building-browser-using-ai-agents-in-python\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/2026\\\/06\\\/22\\\/building-browser-using-ai-agents-in-python\\\/\",\"url\":\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/2026\\\/06\\\/22\\\/building-browser-using-ai-agents-in-python\\\/\",\"name\":\"Constructing Browser-Utilizing AI Brokers in Python - Future News 24\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/futurenews24.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/2026\\\/06\\\/22\\\/building-browser-using-ai-agents-in-python\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/2026\\\/06\\\/22\\\/building-browser-using-ai-agents-in-python\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/machinelearningmastery.com\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png\",\"datePublished\":\"2026-06-22T12:00:00+00:00\",\"dateModified\":\"2026-06-22T12:59:35+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/2026\\\/06\\\/22\\\/building-browser-using-ai-agents-in-python\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/2026\\\/06\\\/22\\\/building-browser-using-ai-agents-in-python\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/2026\\\/06\\\/22\\\/building-browser-using-ai-agents-in-python\\\/#primaryimage\",\"url\":\"https:\\\/\\\/machinelearningmastery.com\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png\",\"contentUrl\":\"https:\\\/\\\/machinelearningmastery.com\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/2026\\\/06\\\/22\\\/building-browser-using-ai-agents-in-python\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/futurenews24.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Constructing Browser-Utilizing AI Brokers in Python\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/futurenews24.com\\\/#website\",\"url\":\"https:\\\/\\\/futurenews24.com\\\/\",\"name\":\"Future News 24\",\"description\":\"The Smart Hub for AI and Next-Gen Innovation\",\"publisher\":{\"@id\":\"https:\\\/\\\/futurenews24.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/futurenews24.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/futurenews24.com\\\/#organization\",\"name\":\"Future News 24\",\"url\":\"https:\\\/\\\/futurenews24.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/futurenews24.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/futurenews24.com\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/fn24-favicon.png\",\"contentUrl\":\"https:\\\/\\\/futurenews24.com\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/fn24-favicon.png\",\"width\":250,\"height\":250,\"caption\":\"Future News 24\"},\"image\":{\"@id\":\"https:\\\/\\\/futurenews24.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/futurenews24.com\\\/#\\\/schema\\\/person\\\/cecad1bde21cfc357cf70128144d6c83\",\"name\":\"Future News 24\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/d57f07142d73cb5503ab2446ea7bc9ef3d0a5ba378d64a6157692311e42bf097?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/d57f07142d73cb5503ab2446ea7bc9ef3d0a5ba378d64a6157692311e42bf097?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/d57f07142d73cb5503ab2446ea7bc9ef3d0a5ba378d64a6157692311e42bf097?s=96&d=mm&r=g\",\"caption\":\"Future News 24\"},\"sameAs\":[\"https:\\\/\\\/futurenews24.com\"],\"url\":\"https:\\\/\\\/futurenews24.com\\\/index.php\\\/author\\\/mridulpahuja20\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Constructing Browser-Utilizing AI Brokers in Python - Future News 24","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/","og_locale":"en_US","og_type":"article","og_title":"Constructing Browser-Utilizing AI Brokers in Python - Future News 24","og_description":"On this article, you&#8217;ll discover ways to construct AI brokers that may browse and work together with actual web sites utilizing Playwright, browser-use, and LangGraph. Matters we are going to cowl embody: Why Playwright is the suitable basis for browser automation in 2026, and the way it differs from Selenium. Learn how to scrape dynamic, [&hellip;]","og_url":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/","og_site_name":"Future News 24","article_published_time":"2026-06-22T12:00:00+00:00","article_modified_time":"2026-06-22T12:59:35+00:00","og_image":[{"url":"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2026\/06\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png","type":"","width":"","height":""},{"url":"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2026\/06\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png","type":"","width":"","height":""}],"author":"Future News 24","twitter_card":"summary_large_image","twitter_image":"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2026\/06\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png","twitter_misc":{"Written by":"Future News 24","Est. reading time":"48 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/#article","isPartOf":{"@id":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/"},"author":{"name":"Future News 24","@id":"https:\/\/futurenews24.com\/#\/schema\/person\/cecad1bde21cfc357cf70128144d6c83"},"headline":"Constructing Browser-Utilizing AI Brokers in Python","datePublished":"2026-06-22T12:00:00+00:00","dateModified":"2026-06-22T12:59:35+00:00","mainEntityOfPage":{"@id":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/"},"wordCount":9784,"commentCount":0,"publisher":{"@id":"https:\/\/futurenews24.com\/#organization"},"image":{"@id":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/#primaryimage"},"thumbnailUrl":"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2026\/06\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png","keywords":["Agents","BrowserUsing","Building","Python"],"articleSection":["Data Science &amp; MLOps"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/","url":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/","name":"Constructing Browser-Utilizing AI Brokers in Python - Future News 24","isPartOf":{"@id":"https:\/\/futurenews24.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/#primaryimage"},"image":{"@id":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/#primaryimage"},"thumbnailUrl":"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2026\/06\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png","datePublished":"2026-06-22T12:00:00+00:00","dateModified":"2026-06-22T12:59:35+00:00","breadcrumb":{"@id":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/#primaryimage","url":"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2026\/06\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png","contentUrl":"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2026\/06\/MLM-Shittu-Building-Browser-Using-AI-Agents-in-Python-1024x680.png"},{"@type":"BreadcrumbList","@id":"https:\/\/futurenews24.com\/index.php\/2026\/06\/22\/building-browser-using-ai-agents-in-python\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/futurenews24.com\/"},{"@type":"ListItem","position":2,"name":"Constructing Browser-Utilizing AI Brokers in Python"}]},{"@type":"WebSite","@id":"https:\/\/futurenews24.com\/#website","url":"https:\/\/futurenews24.com\/","name":"Future News 24","description":"The Smart Hub for AI and Next-Gen Innovation","publisher":{"@id":"https:\/\/futurenews24.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/futurenews24.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/futurenews24.com\/#organization","name":"Future News 24","url":"https:\/\/futurenews24.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/futurenews24.com\/#\/schema\/logo\/image\/","url":"https:\/\/futurenews24.com\/wp-content\/uploads\/2026\/06\/fn24-favicon.png","contentUrl":"https:\/\/futurenews24.com\/wp-content\/uploads\/2026\/06\/fn24-favicon.png","width":250,"height":250,"caption":"Future News 24"},"image":{"@id":"https:\/\/futurenews24.com\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/futurenews24.com\/#\/schema\/person\/cecad1bde21cfc357cf70128144d6c83","name":"Future News 24","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/d57f07142d73cb5503ab2446ea7bc9ef3d0a5ba378d64a6157692311e42bf097?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/d57f07142d73cb5503ab2446ea7bc9ef3d0a5ba378d64a6157692311e42bf097?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d57f07142d73cb5503ab2446ea7bc9ef3d0a5ba378d64a6157692311e42bf097?s=96&d=mm&r=g","caption":"Future News 24"},"sameAs":["https:\/\/futurenews24.com"],"url":"https:\/\/futurenews24.com\/index.php\/author\/mridulpahuja20\/"}]}},"_links":{"self":[{"href":"https:\/\/futurenews24.com\/index.php\/wp-json\/wp\/v2\/posts\/1338","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/futurenews24.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/futurenews24.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/futurenews24.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/futurenews24.com\/index.php\/wp-json\/wp\/v2\/comments?post=1338"}],"version-history":[{"count":1,"href":"https:\/\/futurenews24.com\/index.php\/wp-json\/wp\/v2\/posts\/1338\/revisions"}],"predecessor-version":[{"id":1339,"href":"https:\/\/futurenews24.com\/index.php\/wp-json\/wp\/v2\/posts\/1338\/revisions\/1339"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/futurenews24.com\/index.php\/wp-json\/wp\/v2\/media\/1340"}],"wp:attachment":[{"href":"https:\/\/futurenews24.com\/index.php\/wp-json\/wp\/v2\/media?parent=1338"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/futurenews24.com\/index.php\/wp-json\/wp\/v2\/categories?post=1338"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/futurenews24.com\/index.php\/wp-json\/wp\/v2\/tags?post=1338"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}