diff --git a/examples/__init__.py b/examples/__init__.py index baf98839..377bade8 100644 --- a/examples/__init__.py +++ b/examples/__init__.py @@ -8,9 +8,6 @@ load_dotenv(override=True) # Make sure we have the required environment variables -BROWSERBASE_CONNECT_URL = os.environ.get( - "BROWSERBASE_CONNECT_URL", "wss://connect.browserbase.com" -) _BROWSERBASE_API_KEY = os.environ.get("BROWSERBASE_API_KEY") if not _BROWSERBASE_API_KEY: raise ValueError("BROWSERBASE_API_KEY is not set in environment") diff --git a/examples/e2e/test_playwright.py b/examples/e2e/test_playwright.py index a08de603..2e58c70a 100644 --- a/examples/e2e/test_playwright.py +++ b/examples/e2e/test_playwright.py @@ -4,6 +4,7 @@ import pytest from dotenv import load_dotenv from playwright.sync_api import Playwright, sync_playwright + from browserbase import Browserbase from .. import ( @@ -20,9 +21,10 @@ load_dotenv() CI = os.getenv("CI", "false").lower() == "true" +MAX_RETRIES = 3 -@pytest.fixture(scope="session") +@pytest.fixture(scope="function") # Changed from "session" to "function" def playwright() -> Generator[Playwright, None, None]: with sync_playwright() as p: yield p diff --git a/examples/e2e/test_selenium.py b/examples/e2e/test_selenium.py new file mode 100644 index 00000000..9e64698e --- /dev/null +++ b/examples/e2e/test_selenium.py @@ -0,0 +1,5 @@ +from .. import selenium_basic + + +def test_selenium_basic() -> None: + selenium_basic.run() diff --git a/examples/playwright_captcha.py b/examples/playwright_captcha.py index da768447..7bc2ff42 100644 --- a/examples/playwright_captcha.py +++ b/examples/playwright_captcha.py @@ -1,12 +1,6 @@ from playwright.sync_api import Playwright, ConsoleMessage, sync_playwright - -from examples import ( - BROWSERBASE_API_KEY, - BROWSERBASE_PROJECT_ID, - BROWSERBASE_CONNECT_URL, - bb, -) +from examples import BROWSERBASE_PROJECT_ID, bb DEFAULT_CAPTCHA_URL = "https://www.google.com/recaptcha/api2/demo" OVERRIDE_TIMEOUT = 60000 # 60 seconds, adjust as needed @@ -19,11 +13,7 @@ def run(playwright: Playwright) -> None: assert session.status == "RUNNING", f"Session status is {session.status}" # Connect to the remote session - connect_url = ( - f"{BROWSERBASE_CONNECT_URL}?sessionId={session.id}&apiKey={BROWSERBASE_API_KEY}" - ) - chromium = playwright.chromium - browser = chromium.connect_over_cdp(connect_url) + browser = playwright.chromium.connect_over_cdp(session.connect_url) context = browser.contexts[0] page = context.pages[0] diff --git a/examples/playwright_contexts.py b/examples/playwright_contexts.py index 6a10846d..610636ff 100644 --- a/examples/playwright_contexts.py +++ b/examples/playwright_contexts.py @@ -1,21 +1,15 @@ import time from typing import Optional +from pydantic import TypeAdapter from playwright.sync_api import Cookie, Browser, Playwright, sync_playwright - -from examples import ( - BROWSERBASE_API_KEY, - BROWSERBASE_PROJECT_ID, - BROWSERBASE_CONNECT_URL, - bb, -) +from examples import BROWSERBASE_PROJECT_ID, bb from browserbase.types.session_create_params import ( BrowserSettings, BrowserSettingsContext, ) - CONTEXT_TEST_URL = "https://www.browserbase.com" SECOND = 1000 @@ -47,10 +41,11 @@ def run(playwright: Playwright) -> None: # Step 2: Creates a session with the context session = bb.sessions.create( project_id=BROWSERBASE_PROJECT_ID, - browser_settings=BrowserSettings( - context=BrowserSettingsContext(id=context_id, persist=True), + browser_settings=TypeAdapter(BrowserSettings).validate_python( + {"context": {"id": context_id, "persist": True}} ), ) + print(session) assert ( session.context_id == context_id @@ -59,9 +54,7 @@ def run(playwright: Playwright) -> None: # Step 3: Populates and persists the context print(f"Populating context {context_id} during session {session_id}") - connect_url = ( - f"{BROWSERBASE_CONNECT_URL}?sessionId={session_id}&apiKey={BROWSERBASE_API_KEY}" - ) + connect_url = session.connect_url browser = playwright.chromium.connect_over_cdp(connect_url) page = browser.contexts[0].pages[0] @@ -108,9 +101,7 @@ def run(playwright: Playwright) -> None: # Step 5: Uses context to find previous state print(f"Reusing context {context_id} during session {session_id}") - connect_url = ( - f"{BROWSERBASE_CONNECT_URL}?sessionId={session_id}&apiKey={BROWSERBASE_API_KEY}" - ) + connect_url = session.connect_url browser = playwright.chromium.connect_over_cdp(connect_url) page = browser.contexts[0].pages[0] diff --git a/examples/playwright_downloads.py b/examples/playwright_downloads.py index 3e1e3401..5305d308 100644 --- a/examples/playwright_downloads.py +++ b/examples/playwright_downloads.py @@ -4,13 +4,7 @@ from playwright.sync_api import Playwright, sync_playwright - -from examples import ( - BROWSERBASE_API_KEY, - BROWSERBASE_PROJECT_ID, - BROWSERBASE_CONNECT_URL, - bb, -) +from examples import BROWSERBASE_PROJECT_ID, bb download_re = re.compile(r"sandstorm-(\d{13})+\.mp3") @@ -27,9 +21,7 @@ def run(playwright: Playwright) -> None: assert session.status == "RUNNING", f"Session status is {session.status}" # Connect to the remote session - connect_url = ( - f"{BROWSERBASE_CONNECT_URL}?sessionId={session.id}&apiKey={BROWSERBASE_API_KEY}" - ) + connect_url = session.connect_url browser = playwright.chromium.connect_over_cdp(connect_url) context = browser.contexts[0] page = context.pages[0] diff --git a/examples/playwright_extensions.py b/examples/playwright_extensions.py index d12f47a0..7bdb9426 100644 --- a/examples/playwright_extensions.py +++ b/examples/playwright_extensions.py @@ -7,9 +7,7 @@ from playwright.sync_api import Page, Playwright, sync_playwright from examples import ( - BROWSERBASE_API_KEY, BROWSERBASE_PROJECT_ID, - BROWSERBASE_CONNECT_URL, bb, ) from browserbase.types import Extension, SessionCreateResponse @@ -100,9 +98,7 @@ def run(playwright: Playwright) -> None: extension_id=extension.id, ) - browser = playwright.chromium.connect_over_cdp( - f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}" - ) + browser = playwright.chromium.connect_over_cdp(session.connect_url) context = browser.contexts[0] page = context.pages[0] check_for_message(page, expected_message) @@ -116,9 +112,7 @@ def run(playwright: Playwright) -> None: proxies=True, ) - browser = playwright.chromium.connect_over_cdp( - f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session_with_proxy.id}" - ) + browser = playwright.chromium.connect_over_cdp(session_with_proxy.connect_url) context = browser.contexts[0] page = context.pages[0] diff --git a/examples/playwright_proxy.py b/examples/playwright_proxy.py index 4ea6677b..8378e290 100644 --- a/examples/playwright_proxy.py +++ b/examples/playwright_proxy.py @@ -3,9 +3,7 @@ from playwright.sync_api import Page, Playwright, sync_playwright from examples import ( - BROWSERBASE_API_KEY, BROWSERBASE_PROJECT_ID, - BROWSERBASE_CONNECT_URL, bb, ) @@ -26,9 +24,7 @@ def check_proxy_bytes(session_id: str) -> None: def run_enable_via_create_session(playwright: Playwright) -> None: session = bb.sessions.create(project_id=BROWSERBASE_PROJECT_ID, proxies=True) - browser = playwright.chromium.connect_over_cdp( - f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}" - ) + browser = playwright.chromium.connect_over_cdp(session.connect_url) context = browser.contexts[0] page = context.pages[0] @@ -45,9 +41,7 @@ def run_enable_via_create_session(playwright: Playwright) -> None: def run_enable_via_querystring_with_created_session(playwright: Playwright) -> None: session = bb.sessions.create(project_id=BROWSERBASE_PROJECT_ID, proxies=True) - browser = playwright.chromium.connect_over_cdp( - f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}&enableProxy=true" - ) + browser = playwright.chromium.connect_over_cdp(session.connect_url) context = browser.contexts[0] page = context.pages[0] @@ -84,9 +78,7 @@ def run_geolocation_country(playwright: Playwright) -> None: ], ) - browser = playwright.chromium.connect_over_cdp( - f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}" - ) + browser = playwright.chromium.connect_over_cdp(session.connect_url) context = browser.contexts[0] page = context.pages[0] @@ -113,9 +105,7 @@ def run_geolocation_state(playwright: Playwright) -> None: ], ) - browser = playwright.chromium.connect_over_cdp( - f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}" - ) + browser = playwright.chromium.connect_over_cdp(session.connect_url) context = browser.contexts[0] page = context.pages[0] @@ -143,9 +133,7 @@ def run_geolocation_american_city(playwright: Playwright) -> None: ], ) - browser = playwright.chromium.connect_over_cdp( - f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}" - ) + browser = playwright.chromium.connect_over_cdp(session.connect_url) context = browser.contexts[0] page = context.pages[0] @@ -172,9 +160,7 @@ def run_geolocation_non_american_city(playwright: Playwright) -> None: ], ) - browser = playwright.chromium.connect_over_cdp( - f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}" - ) + browser = playwright.chromium.connect_over_cdp(session.connect_url) context = browser.contexts[0] page = context.pages[0] diff --git a/examples/playwright_upload.py b/examples/playwright_upload.py index c1a2237c..da1e32c0 100644 --- a/examples/playwright_upload.py +++ b/examples/playwright_upload.py @@ -2,12 +2,7 @@ from playwright.sync_api import Playwright, sync_playwright -from examples import ( - BROWSERBASE_API_KEY, - BROWSERBASE_PROJECT_ID, - BROWSERBASE_CONNECT_URL, - bb, -) +from examples import BROWSERBASE_PROJECT_ID, bb PATH_TO_UPLOAD = Path.cwd() / "examples" / "packages" / "logo.png" @@ -16,13 +11,8 @@ def run(playwright: Playwright) -> None: # Create a session session = bb.sessions.create(project_id=BROWSERBASE_PROJECT_ID) - # Construct the URL - url = ( - f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}" - ) - # Connect to the browser - browser = playwright.chromium.connect_over_cdp(url) + browser = playwright.chromium.connect_over_cdp(session.connect_url) context = browser.contexts[0] page = context.pages[0] diff --git a/examples/selenium_basic.py b/examples/selenium_basic.py new file mode 100644 index 00000000..83b73078 --- /dev/null +++ b/examples/selenium_basic.py @@ -0,0 +1,59 @@ +from typing import Dict + +from selenium import webdriver +from selenium.webdriver.remote.remote_connection import RemoteConnection + +from examples import BROWSERBASE_API_KEY, BROWSERBASE_PROJECT_ID, bb + + +class BrowserbaseConnection(RemoteConnection): + """ + Manage a single session with Browserbase. + """ + + session_id: str + + def __init__(self, session_id: str, *args, **kwargs): # type: ignore + super().__init__(*args, **kwargs) # type: ignore + self.session_id = session_id + + def get_remote_connection_headers( # type: ignore + self, parsed_url: str, keep_alive: bool = False + ) -> Dict[str, str]: + headers = super().get_remote_connection_headers(parsed_url, keep_alive) # type: ignore + + # Update headers to include the Browserbase required information + headers["x-bb-api-key"] = BROWSERBASE_API_KEY + headers["session-id"] = self.session_id + + return headers # type: ignore + + +def run() -> None: + # Use the custom class to create and connect to a new browser session + session = bb.sessions.create(project_id=BROWSERBASE_PROJECT_ID) + connection = BrowserbaseConnection(session.id, session.selenium_remote_url) + driver = webdriver.Remote( + command_executor=connection, options=webdriver.ChromeOptions() # type: ignore + ) + + # Print a bit of info about the browser we've connected to + print( + "Connected to Browserbase", + f"{driver.name} version {driver.caps['browserVersion']}", # type: ignore + ) + + try: + # Perform our browser commands + driver.get("https://www.sfmoma.org") + print(f"At URL: {driver.current_url} | Title: {driver.title}") + assert driver.current_url == "https://www.sfmoma.org/" + assert driver.title == "SFMOMA" + + finally: + # Make sure to quit the driver so your session is ended! + driver.quit() + + +if __name__ == "__main__": + run() diff --git a/pyproject.toml b/pyproject.toml index cb341e0a..052598c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,6 +59,7 @@ dev-dependencies = [ "rich>=13.7.1", "python-dotenv", "playwright", + "selenium", ] [tool.rye.scripts] diff --git a/requirements-dev.lock b/requirements-dev.lock index 3bd5de58..0be40775 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -18,11 +18,14 @@ anyio==4.4.0 argcomplete==3.1.2 # via nox attrs==23.1.0 + # via outcome # via pytest + # via trio certifi==2023.7.22 # via httpcore # via httpx # via requests + # via selenium charset-normalizer==3.4.0 # via requests colorlog==6.7.0 @@ -34,12 +37,15 @@ distro==1.8.0 # via browserbase exceptiongroup==1.1.3 # via anyio + # via trio + # via trio-websocket filelock==3.12.4 # via virtualenv greenlet==3.1.1 # via playwright h11==0.14.0 # via httpcore + # via wsproto httpcore==1.0.2 # via httpx httpx==0.25.2 @@ -49,6 +55,7 @@ idna==3.4 # via anyio # via httpx # via requests + # via trio importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest @@ -62,6 +69,8 @@ mypy-extensions==1.0.0 nodeenv==1.8.0 # via pyright nox==2023.4.22 +outcome==1.3.0.post0 + # via trio packaging==23.2 # via nox # via pytest @@ -82,6 +91,8 @@ pyee==12.0.0 pygments==2.18.0 # via rich pyright==1.1.380 +pysocks==1.7.1 + # via urllib3 pytest==7.1.1 # via pytest-asyncio # via pytest-base-url @@ -102,6 +113,7 @@ requests==2.32.3 respx==0.20.2 rich==13.7.1 ruff==0.6.9 +selenium==4.16.0 setuptools==68.2.2 # via nodeenv six==1.16.0 @@ -110,12 +122,20 @@ sniffio==1.3.0 # via anyio # via browserbase # via httpx + # via trio +sortedcontainers==2.4.0 + # via trio text-unidecode==1.3 # via python-slugify time-machine==2.9.0 tomli==2.0.1 # via mypy # via pytest +trio==0.24.0 + # via selenium + # via trio-websocket +trio-websocket==0.11.1 + # via selenium typing-extensions==4.8.0 # via anyio # via browserbase @@ -125,7 +145,10 @@ typing-extensions==4.8.0 # via pyee urllib3==2.2.3 # via requests + # via selenium virtualenv==20.24.5 # via nox +wsproto==1.2.0 + # via trio-websocket zipp==3.17.0 # via importlib-metadata