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 ()
0 commit comments