{ "cells": [ { "cell_type": "markdown", "id": "dfd434e5", "metadata": {}, "source": [ "# Finding the Cheapest Airbnb with AI-Powered Web Automation\n", "\n", "This notebook demonstrates how to build an intelligent web automation agent that searches Airbnb, extracts prices, and finds the cheapest listing in Stockholm. You'll learn how to integrate **Playwright** with **Browser-Use** for powerful AI-driven automation.\n", "\n", "## What You'll Learn:\n", "1. **Playwright + Browser-Use Integration**: Combining browser management with AI automation\n", "2. **Vision-Based Price Extraction**: Let AI \"see\" and read prices from web pages\n", "3. **Structured Data Extraction**: Extract listing data with type-safe Pydantic models\n", "4. **Price Comparison Logic**: Find the cheapest option from multiple listings\n", "5. **Real-world Application**: Practical price comparison automation\n", "\n", "## Prerequisites:\n", "- Azure OpenAI deployment configured\n", "- Playwright installed (`pip install playwright`)\n", "- Understanding of async Python\n", "- Basic web automation concepts" ] }, { "cell_type": "markdown", "id": "6340fa32", "metadata": {}, "source": [ "## Understanding the Playwright + Browser-Use Architecture\n", "\n", "This notebook uses the **official Playwright integration** pattern from Browser-Use documentation.\n", "\n", "### Architecture Flow:\n", "```\n", "┌──────────────────┐\n", "│ Playwright │ ◄─── Manages browser lifecycle\n", "│ Browser Manager │ Handles CDP connection\n", "└────────┬─────────┘ Provides browser instance\n", " │\n", " │ playwright_browser parameter\n", " ▼\n", "┌──────────────────┐\n", "│ Browser-Use │ ◄─── AI-powered automation\n", "│ Browser Object │ Wraps Playwright browser\n", "└────────┬─────────┘ Provides Agent interface\n", " │\n", " │ uses\n", " ▼\n", "┌──────────────────┐\n", "│ Agent │ ◄─── Vision + Decision Making\n", "│ (with LLM) │ Structured output extraction\n", "└──────────────────┘ Natural language tasks\n", " │\n", " │ powered by\n", " ▼\n", "┌──────────────────┐\n", "│ Azure OpenAI │ ◄─── GPT-4 Vision\n", "│ (LLM + Vision) │ Analyzes screenshots\n", "└──────────────────┘ Extracts structured data\n", "```\n", "\n", "### Why This Approach?\n", "\n", "**Playwright provides:**\n", "- ✅ Robust browser lifecycle management\n", "- ✅ Full Chrome DevTools Protocol control\n", "- ✅ Stable page and context handling\n", "- ✅ Built-in waiting and synchronization\n", "\n", "**Browser-Use adds:**\n", "- ✅ AI-powered element finding (no CSS selectors needed!)\n", "- ✅ Vision-based page understanding\n", "- ✅ Structured output extraction with Pydantic\n", "- ✅ Natural language task execution\n", "\n", "**Together they enable:**\n", "- 🎯 \"Search for Stockholm Airbnb\" → Agent navigates\n", "- 👁️ Vision reads all prices on the page\n", "- 📊 Structured extraction → clean Python objects\n", "- 💰 Price comparison logic → find cheapest\n", "\n", "### Our Task Flow:\n", "1. **Playwright** launches Chrome browser\n", "2. **Browser-Use Agent** navigates to Airbnb.com\n", "3. **Agent searches** for \"Stockholm, Sweden\"\n", "4. **Vision model** reads and extracts all listing prices\n", "5. **Structured output** returns typed data (Pydantic models)\n", "6. **Python code** compares prices and finds cheapest\n", "7. **Display results** with rich formatting" ] }, { "cell_type": "code", "execution_count": null, "id": "e1449801", "metadata": {}, "outputs": [], "source": [ "pip install browser_use langchain-openai playwright " ] }, { "cell_type": "code", "execution_count": 2, "id": "290efc2a", "metadata": {}, "outputs": [], "source": [ "!playwright install chromium" ] }, { "cell_type": "code", "execution_count": 12, "id": "ed8648b4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "✅ All packages imported successfully\n" ] } ], "source": [ "import asyncio\n", "import os\n", "import re\n", "from typing import Optional, List\n", "from IPython.display import display, HTML, Markdown\n", "from dotenv import load_dotenv\n", "\n", "# Playwright imports\n", "from playwright.async_api import async_playwright\n", "\n", "# Browser-Use imports - USE BROWSER-USE'S AZURE OPENAI!\n", "# Changed from langchain_openai\n", "from browser_use import Agent, Browser, ChatAzureOpenAI\n", "from pydantic import BaseModel, Field\n", "\n", "print(\"✅ All packages imported successfully\")" ] }, { "cell_type": "code", "execution_count": 4, "id": "26f7e392", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "✅ Azure OpenAI Configuration:\n", " Endpoint: https://foundry-aiteam2510.cognitiveservices.azure.com/\n", " Deployment: gpt-4.1-mini\n", " API Version: 2024-12-01-preview\n" ] } ], "source": [ "# Load environment variables\n", "load_dotenv()\n", "\n", "# Azure OpenAI Configuration\n", "azure_openai_deployment = os.getenv(\"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME\")\n", "azure_openai_endpoint = os.getenv(\"AZURE_OPENAI_ENDPOINT\")\n", "azure_openai_api_key = os.getenv(\"AZURE_OPENAI_API_KEY\")\n", "api_version = os.getenv(\"AZURE_OPENAI_API_VERSION\")\n", "\n", "# Verify configuration\n", "print(\"✅ Azure OpenAI Configuration:\")\n", "print(f\" Endpoint: {azure_openai_endpoint}\")\n", "print(f\" Deployment: {azure_openai_deployment}\")\n", "print(f\" API Version: {api_version}\")" ] }, { "cell_type": "markdown", "id": "70b80842", "metadata": {}, "source": [ "## Initialize Azure OpenAI LLM\n", "\n", "The LLM powers the Agent's decision-making and vision capabilities. We use:\n", "- **Temperature: 0.3** for consistent, predictable automation\n", "- **Vision capabilities** to \"see\" and understand page content\n", "- **Structured output** to extract data into Pydantic models" ] }, { "cell_type": "code", "execution_count": 13, "id": "8ce29377", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "✅ LLM initialized successfully\n", " Model: gpt-4.1-mini\n", " Endpoint: https://foundry-aiteam2510.cognitiveservices.azure.com/\n", " Integration: Browser-Use ChatAzureOpenAI\n" ] } ], "source": [ "# Initialize Azure OpenAI with Browser-Use's ChatAzureOpenAI\n", "llm = ChatAzureOpenAI(\n", " # Your deployment name (e.g., 'gpt-4o', 'gpt-4.1-mini')\n", " model=azure_openai_deployment,\n", " # Browser-Use reads these from environment variables automatically:\n", " # AZURE_OPENAI_ENDPOINT\n", " # AZURE_OPENAI_API_KEY\n", " # AZURE_OPENAI_API_VERSION (optional, defaults to latest)\n", ")\n", "\n", "print(\"✅ LLM initialized successfully\")\n", "print(f\" Model: {azure_openai_deployment}\")\n", "print(f\" Endpoint: {azure_openai_endpoint}\")\n", "print(f\" Integration: Browser-Use ChatAzureOpenAI\")" ] }, { "cell_type": "markdown", "id": "846bf74e", "metadata": {}, "source": [ "## Define Structured Output Models\n", "\n", "We use Pydantic models to extract structured data from Airbnb search results. The Agent will use GPT-4 Vision to read the page and extract data into these models automatically.\n", "\n", "This ensures type safety and validation of all extracted data." ] }, { "cell_type": "code", "execution_count": 26, "id": "5ff0e177", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "✅ Structured output models defined\n", " AirbnbListing: Individual listing data with clickable URLs\n", " SearchResult: Complete search results with price analysis\n" ] } ], "source": [ "# UPDATE THIS CELL - Add URL field to AirbnbListing\n", "class AirbnbListing(BaseModel):\n", " \"\"\"Single Airbnb listing with price information\"\"\"\n", " title: str = Field(description=\"Name/title of the listing\")\n", " price_per_night: float = Field(\n", " description=\"Price per night as a number (extract just the numeric value, ignore currency symbols)\")\n", " currency: str = Field(\n", " default=\"SEK\", description=\"Currency code (SEK for Swedish Krona)\")\n", " rating: Optional[float] = Field(\n", " default=None, description=\"Rating score if visible\")\n", " url: Optional[str] = Field(\n", " default=None, description=\"Full URL link to the listing page\") # ✅ NEW!\n", "\n", "\n", "class SearchResult(BaseModel):\n", " \"\"\"Complete search results from Airbnb\"\"\"\n", " location: str = Field(description=\"Search location (Stockholm, Sweden)\")\n", " total_listings_found: int = Field(\n", " description=\"Number of listings found on the page\")\n", " listings: List[AirbnbListing] = Field(\n", " description=\"List of all listings with prices extracted from the page\")\n", " cheapest_listing: AirbnbListing = Field(\n", " description=\"The listing with the lowest price per night\")\n", " average_price: float = Field(\n", " description=\"Average price per night across all listings\")\n", " price_range: str = Field(description=\"Price range as 'min - max SEK'\")\n", "\n", "\n", "print(\"✅ Structured output models defined\")\n", "print(\" AirbnbListing: Individual listing data with clickable URLs\")\n", "print(\" SearchResult: Complete search results with price analysis\")" ] }, { "cell_type": "markdown", "id": "c50e9241", "metadata": {}, "source": [ "## Helper Functions for Display\n", "\n", "These functions provide rich, educational output in the notebook with formatted HTML." ] }, { "cell_type": "markdown", "id": "61e58afe", "metadata": {}, "source": [] }, { "cell_type": "code", "execution_count": 27, "id": "86977630", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "✅ Structured output models defined\n", " ListingInfo: Extract listing details\n", " BookingDates: Capture selected dates\n", " BookingResult: Final booking status\n" ] } ], "source": [ "class ListingInfo(BaseModel):\n", " \"\"\"Information about the Airbnb listing\"\"\"\n", " title: str = Field(description=\"The name/title of the listing\")\n", " location: str = Field(description=\"City and country of the listing\")\n", " price_per_night: Optional[str] = Field(\n", " description=\"Price per night if visible\")\n", " rating: Optional[str] = Field(description=\"Rating score if visible\")\n", "\n", "\n", "class BookingDates(BaseModel):\n", " \"\"\"Selected booking dates\"\"\"\n", " check_in: str = Field(\n", " description=\"Check-in date in format: Month DD, YYYY\")\n", " check_out: str = Field(\n", " description=\"Check-out date in format: Month DD, YYYY\")\n", " nights: int = Field(description=\"Number of nights\")\n", "\n", "\n", "class BookingResult(BaseModel):\n", " \"\"\"Complete booking result information\"\"\"\n", " success: bool = Field(description=\"Whether the booking flow was completed\")\n", " listing_info: Optional[ListingInfo] = Field(\n", " description=\"Details about the listing\")\n", " booking_dates: Optional[BookingDates] = Field(description=\"Selected dates\")\n", " total_price: Optional[str] = Field(description=\"Total price if shown\")\n", " message: str = Field(description=\"Status message or error description\")\n", "\n", "\n", "print(\"✅ Structured output models defined\")\n", "print(\" ListingInfo: Extract listing details\")\n", "print(\" BookingDates: Capture selected dates\")\n", "print(\" BookingResult: Final booking status\")" ] }, { "cell_type": "markdown", "id": "cd44b2e8", "metadata": {}, "source": [ "## Helper Functions for Display\n", "\n", "These functions provide rich, educational output in the notebook." ] }, { "cell_type": "code", "execution_count": 28, "id": "cc83f640", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "✅ Helper functions loaded\n" ] } ], "source": [ "def display_step(step_number: int, title: str, description: str, color: str = \"#2E8B57\"):\n", " \"\"\"Display a workflow step with formatting\"\"\"\n", " html = f\"\"\"\n", "
{description}
\n", "{caption}
\" if caption else \"\"}\n", "\n",
" This demo uses CDP (Chrome DevTools Protocol) integration:
\n",
" • Chrome runs with remote debugging enabled
\n",
" • Playwright connects to Chrome via CDP
\n",
" • Browser-Use connects to same Chrome via CDP
\n",
" • GPT-4 Vision reads and extracts prices
\n",
" • Structured Output returns type-safe data\n",
"
\n", " 📊 Watch the browser as the AI agent searches and analyzes prices!\n", "
\n", "Using AI agent with vision to navigate Airbnb and search for Stockholm listings. The agent will handle pop-ups, cookie banners, and search automatically.
\n", "Using GPT-4 Vision to read all listing prices from the page and extract structured data into Pydantic models. The AI 'sees' the page like a human.
\n", "| Location: | \n", "Stockholm, Sweden | \n", "
| Total Listings Found: | \n", "18 | \n", "
| Average Price: | \n", "572.72 SEK/night | \n", "
| Price Range: | \n", "257 - 1066 SEK | \n", "
\n", " 257.00 SEK/night\n", "
\n", "⭐ Rating: 4.84/5.0
\n", "\n", " 💰 Saves you 315.72 SEK compared to average price!\n", "
\n", " \n", " \n", " 🔗 View Listing on Airbnb\n", " \n", " \n", "| Rank | \n", "Listing | \n", "Price/Night | \n", "Rating | \n", "Link | \n", "
|---|---|---|---|---|
| 🏆 1 | \n", "Room in Nacka... | \n", "\n", " 257.00 SEK\n", " | \n", "\n", " ⭐ 4.84\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 2 | \n", "Shared hotel room in Stockholms kommun... | \n", "\n", " 258.00 SEK\n", " | \n", "\n", " ⭐ 4.42\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 3 | \n", "Shared room in Stockholms kommun... | \n", "\n", " 274.00 SEK\n", " | \n", "\n", " ⭐ 4.68\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 4 | \n", "Shared hotel room in Stockholms kommun... | \n", "\n", " 282.00 SEK\n", " | \n", "\n", " ⭐ 4.6\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 5 | \n", "Shared room in Stockholms kommun... | \n", "\n", " 294.00 SEK\n", " | \n", "\n", " ⭐ 4.76\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 6 | \n", "Shared hotel room in Stockholms kommun... | \n", "\n", " 294.00 SEK\n", " | \n", "\n", " ⭐ 4.62\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 7 | \n", "Room in Stockholms kommun... | \n", "\n", " 317.00 SEK\n", " | \n", "\n", " ⭐ 4.93\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 8 | \n", "Shared hotel room in Stockholms kommun... | \n", "\n", " 352.00 SEK\n", " | \n", "\n", " N/A\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 9 | \n", "Room in Stockholms kommun... | \n", "\n", " 353.00 SEK\n", " | \n", "\n", " ⭐ 5.0\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 10 | \n", "Rooms in Enskede - Årsta - Vantör... | \n", "\n", " 365.00 SEK\n", " | \n", "\n", " ⭐ 4.76\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 11 | \n", "Hostel in Norrmalm... | \n", "\n", " 368.00 SEK\n", " | \n", "\n", " ⭐ 4.46\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 12 | \n", "Room in Helenelund... | \n", "\n", " 383.00 SEK\n", " | \n", "\n", " ⭐ 4.87\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 13 | \n", "Apartment in Sjöberg... | \n", "\n", " 851.00 SEK\n", " | \n", "\n", " ⭐ 4.29\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 14 | \n", "Apartment in Solna kommun... | \n", "\n", " 929.00 SEK\n", " | \n", "\n", " ⭐ 4.89\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 15 | \n", "Apartment in Stockholms kommun... | \n", "\n", " 939.00 SEK\n", " | \n", "\n", " N/A\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 16 | \n", "Apartment in Stockholms kommun... | \n", "\n", " 966.00 SEK\n", " | \n", "\n", " ⭐ 4.37\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 17 | \n", "Apartment in Stockholms kommun... | \n", "\n", " 1008.00 SEK\n", " | \n", "\n", " ⭐ 4.79\n", " | \n", "\n", " 🔗 View\n", " | \n", "
| 18 | \n", "Apartment in Stockholms kommun... | \n", "\n", " 1066.00 SEK\n", " | \n", "\n", " ⭐ 4.77\n", " | \n", "\n", " 🔗 View\n", " | \n", "
\n", " 💡 Tip: Click on any listing title or the \"View\" button to open it in a new tab\n", "
\n", "\n",
" This demo uses CDP (Chrome DevTools Protocol) integration:
\n",
" • Chrome runs with remote debugging enabled
\n",
" • Playwright connects to Chrome via CDP
\n",
" • Browser-Use connects to same Chrome via CDP
\n",
" • GPT-4 Vision reads and extracts prices
\n",
" • Structured Output returns type-safe data\n",
"
\n", " 📊 Watch the browser as the AI agent searches and analyzes prices!\n", "
\n", "| Location: | \n", "{result.location} | \n", "
| Total Listings Found: | \n", "{result.total_listings_found} | \n", "
| Average Price: | \n", "{result.average_price:.2f} SEK/night | \n", "
| Price Range: | \n", "{result.price_range} | \n", "
\n", " {cheapest.price_per_night:.2f} {cheapest.currency}/night\n", "
\n", " {f\"⭐ Rating: {cheapest.rating}/5.0
\" if cheapest.rating else \"\"}\n", "\n", " 💰 Saves you {(result.average_price - cheapest.price_per_night):.2f} SEK compared to average price!\n", "
\n", " {view_button}\n", "| Rank | \n", "Listing | \n", "Price/Night | \n", "Rating | \n", "Link | \n", "
|---|---|---|---|---|
| {badge}{idx} | \n", "{title_html} | \n", "\n", " {listing.price_per_night:.2f} {listing.currency}\n", " | \n", "\n", " {f\"⭐ {listing.rating}\" if listing.rating else \"N/A\"}\n", " | \n", "\n", " {link_html}\n", " | \n", "
\n", " 💡 Tip: Click on any listing title or the \"View\" button to open it in a new tab\n", "
\n", "