diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md index af776c0679c..56dae0d5562 100644 --- a/examples/cdp_mode/ReadMe.md +++ b/examples/cdp_mode/ReadMe.md @@ -420,7 +420,7 @@ sb.cdp.js_dumps(obj_name) sb.cdp.maximize() sb.cdp.minimize() sb.cdp.medimize() -sb.cdp.set_window_rect() +sb.cdp.set_window_rect(x, y, width, height) sb.cdp.reset_window_size() sb.cdp.open_new_window(url=None, switch_to=True) sb.cdp.switch_to_window(window) diff --git a/examples/cdp_mode/raw_geolocation.py b/examples/cdp_mode/raw_geolocation.py new file mode 100644 index 00000000000..7a3230efc56 --- /dev/null +++ b/examples/cdp_mode/raw_geolocation.py @@ -0,0 +1,16 @@ +"""Geolocation example using CDP Mode without WebDriver""" +from seleniumbase import decorators +from seleniumbase import sb_cdp + + +@decorators.print_runtime("Geolocation CDP Example") +def main(): + url = "https://www.openstreetmap.org/" + sb = sb_cdp.Chrome(url, geoloc=(48.87645, 2.26340)) + sb.click("span.geolocate") + sb.assert_url_contains("48.876450/2.263400") + sb.sleep(5) + + +if __name__ == "__main__": + main() diff --git a/examples/cdp_mode/raw_timezone.py b/examples/cdp_mode/raw_timezone.py new file mode 100644 index 00000000000..7f269040b66 --- /dev/null +++ b/examples/cdp_mode/raw_timezone.py @@ -0,0 +1,34 @@ +"""Timezone example using CDP Mode without WebDriver""" +import mycdp +from seleniumbase import decorators +from seleniumbase import sb_cdp + + +async def request_paused_handler(event, tab): + r = event.request + is_image = ".png" in r.url or ".jpg" in r.url or ".gif" in r.url + if not is_image: # Let the data through + tab.feed_cdp(mycdp.fetch.continue_request(request_id=event.request_id)) + else: # Block the data (images) + TIMED_OUT = mycdp.network.ErrorReason.TIMED_OUT + tab.feed_cdp(mycdp.fetch.fail_request(event.request_id, TIMED_OUT)) + + +@decorators.print_runtime("Timezone CDP Example") +def main(): + url = "https://www.randymajors.org/what-time-zone-am-i-in" + sb = sb_cdp.Chrome( + url, + ad_block=True, + lang="bn", + tzone="Asia/Kolkata", + geoloc=(26.855323, 80.937710) + ) + sb.add_handler(mycdp.fetch.RequestPaused, request_paused_handler) + sb.remove_elements("#right-sidebar") + sb.remove_elements('[id*="Footer"]') + sb.sleep(6) + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt index e90a184ebdb..c8a3a9e12aa 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ pip>=25.0.1 packaging>=25.0 setuptools~=70.2;python_version<"3.10" -setuptools>=79.0.0;python_version>="3.10" +setuptools>=79.0.1;python_version>="3.10" wheel>=0.45.1 attrs>=25.3.0 certifi>=2025.1.31 @@ -11,7 +11,7 @@ websockets>=15.0.1;python_version>="3.9" filelock~=3.16.1;python_version<"3.9" filelock>=3.18.0;python_version>="3.9" fasteners>=0.19 -mycdp>=1.1.1 +mycdp>=1.2.0 pynose>=1.5.4 platformdirs>=4.3.6;python_version<"3.9" platformdirs>=4.3.7;python_version>="3.9" @@ -36,7 +36,7 @@ urllib3>=1.26.20,<2;python_version<"3.10" urllib3>=1.26.20,<2.5.0;python_version>="3.10" requests==2.32.3 sniffio==1.3.1 -h11==0.14.0 +h11==0.16.0 outcome==1.3.0.post0 trio==0.27.0;python_version<"3.9" trio==0.30.0;python_version>="3.9" diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index 254f4065e3e..eeed4ff3f51 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.37.6" +__version__ = "4.37.7" diff --git a/seleniumbase/undetected/cdp_driver/browser.py b/seleniumbase/undetected/cdp_driver/browser.py index 1224fcdc530..d5c59d68bd4 100644 --- a/seleniumbase/undetected/cdp_driver/browser.py +++ b/seleniumbase/undetected/cdp_driver/browser.py @@ -287,10 +287,63 @@ async def get( connection: tab.Tab = next( filter(lambda item: item.type_ == "page", self.targets) ) - # Use the tab to navigate to new url if hasattr(sb_config, "_cdp_locale") and sb_config._cdp_locale: await connection.send(cdp.page.navigate("about:blank")) - await connection.set_locale(sb_config._cdp_locale) + if ( + hasattr(sb_config, "_cdp_user_agent") + and sb_config._cdp_user_agent + ): + pass + elif ( + hasattr(sb_config, "_cdp_platform") + and sb_config._cdp_platform + ): + pass + else: + await connection.set_locale(sb_config._cdp_locale) + if hasattr(sb_config, "_cdp_timezone") and sb_config._cdp_timezone: + await connection.send(cdp.page.navigate("about:blank")) + await connection.set_timezone(sb_config._cdp_timezone) + if ( + hasattr(sb_config, "_cdp_user_agent") + and sb_config._cdp_user_agent + ): + await connection.send(cdp.page.navigate("about:blank")) + if hasattr(sb_config, "_cdp_locale") and sb_config._cdp_locale: + _cdp_platform = None + if ( + hasattr(sb_config, "_cdp_platform") + and sb_config._cdp_platform + ): + _cdp_platform = sb_config._cdp_platform + await connection.set_user_agent( + sb_config._cdp_user_agent, + sb_config._cdp_locale, + _cdp_platform, + ) + else: + await connection.set_user_agent(sb_config._cdp_user_agent) + elif ( + hasattr(sb_config, "_cdp_platform") and sb_config._cdp_platform + ): + await connection.send(cdp.page.navigate("about:blank")) + if hasattr(sb_config, "_cdp_locale") and sb_config._cdp_locale: + _cdp_platform = sb_config._cdp_platform + await connection.set_user_agent( + accept_language=sb_config._cdp_locale, + platform=_cdp_platform, + ) + else: + await connection.set_user_agent( + platform=sb_config._cdp_platform + ) + if ( + hasattr(sb_config, "_cdp_geolocation") + and sb_config._cdp_geolocation + ): + await connection.send(cdp.page.navigate("about:blank")) + await connection.set_geolocation(sb_config._cdp_geolocation) + # Use the tab to navigate to new url frame_id, loader_id, *_ = await connection.send( cdp.page.navigate(url) ) diff --git a/seleniumbase/undetected/cdp_driver/cdp_util.py b/seleniumbase/undetected/cdp_driver/cdp_util.py index 29a166b1ad9..b2a5bc16f8a 100644 --- a/seleniumbase/undetected/cdp_driver/cdp_util.py +++ b/seleniumbase/undetected/cdp_driver/cdp_util.py @@ -10,8 +10,10 @@ import typing from contextlib import suppress from seleniumbase import config as sb_config +from seleniumbase import extensions from seleniumbase.config import settings from seleniumbase.core import detect_b_ver +from seleniumbase.core import download_helper from seleniumbase.core import proxy_helper from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils @@ -25,7 +27,10 @@ logger = logging.getLogger(__name__) IS_LINUX = shared_utils.is_linux() +DOWNLOADS_FOLDER = download_helper.get_downloads_folder() PROXY_DIR_LOCK = proxy_helper.PROXY_DIR_LOCK +EXTENSIONS_DIR = os.path.dirname(os.path.realpath(extensions.__file__)) +AD_BLOCK_ZIP_PATH = os.path.join(EXTENSIONS_DIR, "ad_block.zip") T = typing.TypeVar("T") @@ -168,6 +173,19 @@ def __add_chrome_ext_dir(extension_dir, dir_path): return extension_dir +def __unzip_to_new_folder(zip_file, folder): + proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK) + with proxy_dir_lock: + with suppress(Exception): + shared_utils.make_writable(PROXY_DIR_LOCK) + if not os.path.exists(folder): + import zipfile + zip_ref = zipfile.ZipFile(zip_file, "r") + os.makedirs(folder) + zip_ref.extractall(folder) + zip_ref.close() + + def __add_chrome_proxy_extension( extension_dir, proxy_string, @@ -231,6 +249,7 @@ async def start( browser_executable_path: Optional[PathLike] = None, browser_args: Optional[List[str]] = None, xvfb_metrics: Optional[List[str]] = None, # "Width,Height" for Linux + ad_block: Optional[bool] = False, sandbox: Optional[bool] = True, lang: Optional[str] = None, # Set the Language Locale Code host: Optional[str] = None, # Chrome remote-debugging-host @@ -238,7 +257,10 @@ async def start( xvfb: Optional[int] = None, # Use a special virtual display on Linux headed: Optional[bool] = None, # Override default Xvfb mode on Linux expert: Optional[bool] = None, # Open up closed Shadow-root elements + agent: Optional[str] = None, # Set the user-agent string proxy: Optional[str] = None, # "host:port" or "user:pass@host:port" + tzone: Optional[str] = None, # Eg "America/New_York", "Asia/Kolkata" + geoloc: Optional[list | tuple] = None, # Eg (48.87645, 2.26340) extension_dir: Optional[str] = None, # Chrome extension directory **kwargs: Optional[dict], ) -> Browser: @@ -296,6 +318,13 @@ async def start( proxy_user, proxy_pass, ) + if ad_block: + incognito = False + guest = False + ad_block_zip = AD_BLOCK_ZIP_PATH + ad_block_dir = os.path.join(DOWNLOADS_FOLDER, "ad_block") + __unzip_to_new_folder(ad_block_zip, ad_block_dir) + extension_dir = __add_chrome_ext_dir(extension_dir, ad_block_dir) if not config: config = Config( user_data_dir, @@ -329,6 +358,26 @@ async def start( sb_config._cdp_locale = kwargs["locale_code"] else: sb_config._cdp_locale = None + if tzone: + sb_config._cdp_timezone = tzone + elif "timezone" in kwargs: + sb_config._cdp_timezone = kwargs["timezone"] + else: + sb_config._cdp_timezone = None + if geoloc: + sb_config._cdp_geolocation = geoloc + elif "geolocation" in kwargs: + sb_config._cdp_geolocation = kwargs["geolocation"] + else: + sb_config._cdp_geolocation = None + if agent: + sb_config._cdp_user_agent = agent + elif "user_agent" in kwargs: + sb_config._cdp_user_agent = kwargs["user_agent"] + else: + sb_config._cdp_user_agent = None + if "platform" in kwargs: + sb_config._cdp_platform = kwargs["platform"] return driver diff --git a/seleniumbase/undetected/cdp_driver/connection.py b/seleniumbase/undetected/cdp_driver/connection.py index 6d3fa24c839..5b146526691 100644 --- a/seleniumbase/undetected/cdp_driver/connection.py +++ b/seleniumbase/undetected/cdp_driver/connection.py @@ -348,7 +348,37 @@ async def wait(self, t: Union[int, float] = None): async def set_locale(self, locale: Optional[str] = None): """Sets the Language Locale code via set_user_agent_override.""" - await self.send(cdp.network.set_user_agent_override("", locale)) + await self.set_user_agent(user_agent="", accept_language=locale) + + async def set_timezone(self, timezone: Optional[str] = None): + """Sets the Timezone via set_timezone_override.""" + await self.send(cdp.emulation.set_timezone_override(timezone)) + + async def set_user_agent( + self, + user_agent: Optional[str] = "", + accept_language: Optional[str] = None, + platform: Optional[str] = None, # navigator.platform + ): + """Sets the User Agent via set_user_agent_override.""" + if not user_agent: + user_agent = "" + await self.send(cdp.network.set_user_agent_override( + user_agent=user_agent, + accept_language=accept_language, + platform=platform, + )) + + async def set_geolocation(self, geolocation: Optional[tuple] = None): + """Sets the User Agent via set_geolocation_override.""" + await self.send(cdp.browser.grant_permissions( + permissions=["geolocation"], + )) + await self.send(cdp.emulation.set_geolocation_override( + latitude=geolocation[0], + longitude=geolocation[1], + accuracy=100, + )) def __getattr__(self, item): """:meta private:""" diff --git a/setup.py b/setup.py index d03b152d4d0..754f45b6b08 100755 --- a/setup.py +++ b/setup.py @@ -150,7 +150,7 @@ 'pip>=25.0.1', 'packaging>=25.0', 'setuptools~=70.2;python_version<"3.10"', # Newer ones had issues - 'setuptools>=79.0.0;python_version>="3.10"', + 'setuptools>=79.0.1;python_version>="3.10"', 'wheel>=0.45.1', 'attrs>=25.3.0', "certifi>=2025.1.31", @@ -160,7 +160,7 @@ 'filelock~=3.16.1;python_version<"3.9"', 'filelock>=3.18.0;python_version>="3.9"', 'fasteners>=0.19', - "mycdp>=1.1.1", + "mycdp>=1.2.0", "pynose>=1.5.4", 'platformdirs>=4.3.6;python_version<"3.9"', 'platformdirs>=4.3.7;python_version>="3.9"', @@ -185,7 +185,7 @@ 'urllib3>=1.26.20,<2.5.0;python_version>="3.10"', 'requests==2.32.3', 'sniffio==1.3.1', - 'h11==0.14.0', + 'h11==0.16.0', 'outcome==1.3.0.post0', 'trio==0.27.0;python_version<"3.9"', 'trio==0.30.0;python_version>="3.9"',