Skip to content

Commit 124851c

Browse files
committed
add browserapi
1 parent c1d4de8 commit 124851c

3 files changed

Lines changed: 170 additions & 0 deletions

File tree

brightdata/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
from .brightdata_web_unlocker import BrightdataWebUnlocker
44
from .base_specialized_scraper import BrightdataBaseSpecializedScraper
55
from .auto import scrape_url, trigger_scrape_url, trigger_scrape_url_with_fallback
6+
from .browser_api import BrowserAPI
67

brightdata/browser_api.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
from selenium.webdriver import Remote, ChromeOptions
2+
from selenium.webdriver.chromium.remote_connection import ChromiumRemoteConnection
3+
from selenium.webdriver.common.by import By
4+
from selenium.webdriver.support.ui import WebDriverWait
5+
from selenium.webdriver.support import expected_conditions as EC
6+
import time
7+
from dotenv import load_dotenv
8+
import os
9+
10+
# BRIGHTDATA_BROWSERAPI_USERNAME
11+
# BRIGHTDATA_BROWSERAPI_PASSWORD
12+
13+
14+
class BrowserAPI:
15+
"""
16+
Wrapper around Bright Data's Browser API (Selenium) with:
17+
• get_page_source()
18+
• get_page_source_and_wait()
19+
• capture_screenshot()
20+
"""
21+
22+
DEFAULT_HOST = "brd.superproxy.io"
23+
DEFAULT_PORT = 9515
24+
25+
def __init__(
26+
self,
27+
username: str | None = None,
28+
password: str | None = None,
29+
host: str = DEFAULT_HOST,
30+
scheme: str = "https",
31+
headless: bool = True,
32+
window_size: tuple[int, int] = (1920, 1080),
33+
):
34+
load_dotenv()
35+
36+
# 1) Load from env if not provided
37+
env_user = os.getenv("BRIGHTDATA_BROWSERAPI_USERNAME")
38+
env_pass = os.getenv("BRIGHTDATA_BROWSERAPI_PASSWORD")
39+
40+
self.username = username or env_user
41+
self.password = password or env_pass
42+
43+
if not (self.username and self.password):
44+
raise ValueError(
45+
"BrowserAPI requires username & password; "
46+
"set BRIGHTDATA_BROWSERAPI_USERNAME and BRIGHTDATA_BROWSERAPI_PASSWORD"
47+
"username should look like this:"
48+
"brd-customer-hl_1434343-zone-scraping_browser1"
49+
)
50+
51+
# 2) Build endpoint with fixed port
52+
self._endpoint = f"{scheme}://{self.username}:{self.password}@{host}:{self.DEFAULT_PORT}"
53+
54+
opts = ChromeOptions()
55+
if headless:
56+
opts.add_argument("--headless=new")
57+
opts.add_argument(f"--window-size={window_size[0]},{window_size[1]}")
58+
self._opts = opts
59+
60+
def _new_driver(self) -> Remote:
61+
conn = ChromiumRemoteConnection(self._endpoint, "goog", "chrome")
62+
return Remote(conn, options=self._opts)
63+
64+
def get_page_source(
65+
self,
66+
url: str,
67+
) -> str:
68+
"""
69+
Navigate to `url` and return the raw page_source immediately.
70+
"""
71+
driver = self._new_driver()
72+
try:
73+
driver.get(url)
74+
return driver.page_source
75+
finally:
76+
driver.quit()
77+
78+
def get_page_source_with_a_delay(
79+
self,
80+
url: str,
81+
wait_time_in_seconds: int = 20,
82+
extra_delay: float = 1.0,
83+
) -> str:
84+
"""
85+
Navigate to `url`, wait until <div id="main"> is present
86+
(SPA shell hydration), then return fully rendered HTML.
87+
"""
88+
driver = self._new_driver()
89+
try:
90+
driver.get(url)
91+
WebDriverWait(driver, wait_time_in_seconds).until(
92+
EC.presence_of_element_located((By.ID, "main"))
93+
)
94+
time.sleep(extra_delay)
95+
return driver.page_source
96+
finally:
97+
driver.quit()
98+
99+
def capture_screenshot(
100+
self,
101+
url: str,
102+
path: str,
103+
wait_for_main: bool = False,
104+
wait_time_in_seconds: int = 20,
105+
extra_delay: float = 1.0,
106+
) -> None:
107+
"""
108+
Navigate to `url` and save a screenshot to `path`.
109+
If wait_for_main is True, first wait until <div id="main"> appears.
110+
"""
111+
driver = self._new_driver()
112+
try:
113+
driver.get(url)
114+
if wait_for_main:
115+
WebDriverWait(driver, wait_time_in_seconds).until(
116+
EC.presence_of_element_located((By.ID, "main"))
117+
)
118+
time.sleep(extra_delay)
119+
driver.get_screenshot_as_file(path)
120+
finally:
121+
driver.quit()
122+
123+
124+
def main():
125+
# AUTH = "brd-customer-hl_1cdf8003-zone-scraping_browser1:f05i50grymt3"
126+
# api = BrowserAPI(AUTH)
127+
128+
129+
130+
api = BrowserAPI(
131+
username="brd-customer-hl_1cdf8003-zone-scraping_browser1",
132+
password="f05i50grymt3",
133+
)
134+
135+
target_page_address="https://budgety.ai"
136+
137+
# 1) Just grab the raw HTML immediately:
138+
# html_raw = api.get_page_source(target_page_address)
139+
# print(html_raw)
140+
141+
142+
# 2) Grab the hydrated HTML by waiting for the headline:
143+
html_hydrated = api.get_page_source_with_a_delay(target_page_address)
144+
print(html_hydrated)
145+
146+
#api.capture_screenshot(target_page_address, "budgety.png", wait_for_main=True, wait_time_in_seconds=30)
147+
148+
149+
if __name__ == "__main__":
150+
main()

brightdata/browser_api_test.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from selenium.webdriver import Remote, ChromeOptions
2+
from selenium.webdriver.chromium.remote_connection import ChromiumRemoteConnection
3+
from selenium.webdriver.common.by import By
4+
AUTH = 'brd-customer-hl_1cdf8003-zone-scraping_browser1:f05i50grymt3'
5+
SBR_WEBDRIVER = f'https://{AUTH}@brd.superproxy.io:9515'
6+
def main():
7+
print('Connecting to Browser API...')
8+
sbr_connection = ChromiumRemoteConnection(SBR_WEBDRIVER, 'goog', 'chrome')
9+
with Remote(sbr_connection, options=ChromeOptions()) as driver:
10+
print('Connected! Navigating...')
11+
# driver.get('https://example.com')
12+
driver.get('https://budgety.ai')
13+
print('Taking page screenshot to file page.png')
14+
driver.get_screenshot_as_file('./page.png')
15+
print('Navigated! Scraping page content...')
16+
html = driver.page_source
17+
print(html)
18+
if __name__ == '__main__':
19+
main()

0 commit comments

Comments
 (0)