diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md index ff91a547414..4db4d5c7df9 100644 --- a/examples/cdp_mode/ReadMe.md +++ b/examples/cdp_mode/ReadMe.md @@ -22,11 +22,12 @@ * Backwards compatibility for existing UC Mode scripts. * More configuration options when launching browsers. * More methods. (And bug-fixes for existing methods.) +* `PyAutoGUI` integration for advanced stealth abilities. * Faster response time for support. (Eg. [Discord Chat](https://discord.gg/EdhQTn3EyE)) -------- -### 🐙 CDP Mode usage: +### 🐙 CDP Mode Usage: * **`sb.activate_cdp_mode(url)`** @@ -34,16 +35,17 @@ That disconnects WebDriver from Chrome (which prevents detection), and gives you access to `sb.cdp` methods (which don't trigger anti-bot checks). -### 🐙 Here are some common `sb.cdp` methods: +### 🐙 Here are a few common `sb.cdp` methods: * `sb.cdp.click(selector)` * `sb.cdp.click_if_visible(selector)` +* `sb.cdp.gui_click_element(selector)` * `sb.cdp.type(selector, text)` * `sb.cdp.press_keys(selector, text)` * `sb.cdp.select_all(selector)` * `sb.cdp.get_text(selector)` -When `type()` is too fast, use the slower `press_keys()` to avoid detection. You can also use `sb.sleep(seconds)` to slow things down. +When `type()` is too fast, use the slower `press_keys()` to avoid detection. You can also use `sb.sleep(seconds)` to slow things down. Methods that start with `sb.cdp.gui` use `PyAutoGUI` for interaction. To use WebDriver methods again, call: @@ -63,17 +65,13 @@ To find out if WebDriver is connected or disconnected, call: -------- -### 🐙 CDP Mode examples: +### 🐙 CDP Mode Examples ([SeleniumBase/examples/cdp_mode](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode)) -> [SeleniumBase/examples/cdp_mode](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode) - -### 🔖 Example 1: (Pokemon site using Incapsula/Imperva protection with invisible reCAPTCHA) - -> [SeleniumBase/examples/cdp_mode/raw_pokemon.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_pokemon.py) +
sb.sleep()
calls have been added to prevent bot-detection because some sites will flag you as a bot if you perform actions too quickly.)
(Note: Some sites may IP-block you for 36 hours or more if they catch you using regular Selenium WebDriver. Be extra careful when creating and/or modifying automation scripts that run on them.)
--------
-### 🐙 CDP Mode API / Methods
+### 🐙 CDP Mode API / Methods
(Some method args have been left out for simplicity. Eg: timeout
)
@@ -323,6 +322,9 @@ sb.cdp.find_visible_elements(selector)
sb.cdp.click_nth_element(selector, number)
sb.cdp.click_nth_visible_element(selector, number)
sb.cdp.click_link(link_text)
+sb.cdp.go_back()
+sb.cdp.go_forward()
+sb.cdp.get_navigation_history()
sb.cdp.tile_windows(windows=None, max_columns=0)
sb.cdp.get_all_cookies(*args, **kwargs)
sb.cdp.set_all_cookies(*args, **kwargs)
@@ -434,7 +436,7 @@ sb.cdp.save_screenshot(name, folder=None, selector=None)
--------
-### 🐙 CDP Mode WebElement API / Methods
+### 🐙 CDP Mode WebElement API / Methods
```python
element.clear_input()
diff --git a/examples/cdp_mode/raw_hyatt.py b/examples/cdp_mode/raw_hyatt.py
index 86bb270fe31..93f10941685 100644
--- a/examples/cdp_mode/raw_hyatt.py
+++ b/examples/cdp_mode/raw_hyatt.py
@@ -19,16 +19,17 @@
sb.sleep(1)
sb.cdp.click('button[data-locator="find-hotels"]')
sb.sleep(5)
- hotel_names = sb.cdp.select_all(
- 'div[data-booking-status="BOOKABLE"] [class*="HotelCard_header"]'
- )
- hotel_prices = sb.cdp.select_all(
- 'div[data-booking-status="BOOKABLE"] div.rate'
- )
- sb.assert_true(len(hotel_names) == len(hotel_prices))
+ card_info = 'div[data-booking-status="BOOKABLE"] [class*="HotelCard_info"]'
+ hotels = sb.cdp.select_all(card_info)
print("Hyatt Hotels in %s:" % location)
print("(" + sb.cdp.get_text("ul.b-color_text-white") + ")")
- if len(hotel_names) == 0:
+ if len(hotels) == 0:
print("No availability over the selected dates!")
- for i, hotel in enumerate(hotel_names):
- print("* %s: %s => %s" % (i + 1, hotel.text, hotel_prices[i].text))
+ for hotel in hotels:
+ info = hotel.text.strip()
+ if "Avg/Night" in info and not info.startswith("Rates from"):
+ name = info.split(" (")[0].split(" + ")[0].split(" Award Cat")[0]
+ price = "?"
+ if "Rates from : " in info:
+ price = info.split("Rates from : ")[1].split(" Avg/Night")[0]
+ print("* %s => %s" % (name, price))
diff --git a/examples/presenter/uc_presentation_4.py b/examples/presenter/uc_presentation_4.py
index b5700836cb4..2f938300215 100644
--- a/examples/presenter/uc_presentation_4.py
+++ b/examples/presenter/uc_presentation_4.py
@@ -695,24 +695,24 @@ def test_presentation_4(self):
sb.sleep(1)
sb.cdp.click('button[data-locator="find-hotels"]')
sb.sleep(5)
- hotel_names = sb.cdp.select_all(
- 'div[data-booking-status="BOOKABLE"]'
- ' [class*="HotelCard_header"]'
+ card_info = (
+ 'div[data-booking-status="BOOKABLE"] [class*="HotelCard_info"]'
)
- hotel_prices = sb.cdp.select_all(
- 'div[data-booking-status="BOOKABLE"] div.rate'
- )
- sb.assert_true(len(hotel_names) == len(hotel_prices))
- print("\n\nHyatt Hotels in %s:" % location)
+ hotels = sb.cdp.select_all(card_info)
+ print("Hyatt Hotels in %s:" % location)
print("(" + sb.cdp.get_text("ul.b-color_text-white") + ")")
- if len(hotel_names) == 0:
+ if len(hotels) == 0:
print("No availability over the selected dates!")
- for i, hotel in enumerate(hotel_names):
- with suppress(Exception):
- print(
- "* %s: %s => %s"
- % (i + 1, hotel.text, hotel_prices[i].text)
- )
+ for hotel in hotels:
+ info = hotel.text.strip()
+ if "Avg/Night" in info and not info.startswith("Rates from"):
+ name = info.split(" (")[0]
+ name = name.split(" + ")[0].split(" Award Cat")[0]
+ price = "?"
+ if "Rates from : " in info:
+ price = info.split("Rates from : ")[1]
+ price = price.split(" Avg/Night")[0]
+ print("* %s => %s" % (name, price))
self.create_presentation(theme="serif", transition="none")
self.add_slide(
diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt
index 1f41bafb9e3..dfb2338b4bf 100644
--- a/mkdocs_build/requirements.txt
+++ b/mkdocs_build/requirements.txt
@@ -20,7 +20,7 @@ lxml==5.3.0
pyquery==2.0.1
readtime==3.0.0
mkdocs==1.6.1
-mkdocs-material==9.5.46
+mkdocs-material==9.5.47
mkdocs-exclude-search==0.6.6
mkdocs-simple-hooks==0.1.5
mkdocs-material-extensions==1.3.1
diff --git a/requirements.txt b/requirements.txt
index 9039a533d6b..95bf5543309 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -44,7 +44,7 @@ execnet==2.1.1
iniconfig==2.0.0
pluggy==1.5.0
py==1.11.0
-pytest==8.3.3
+pytest==8.3.4
pytest-html==2.0.1
pytest-metadata==3.1.1
pytest-ordering==0.6
diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py
index ed335a24092..60922fb1d81 100755
--- a/seleniumbase/__version__.py
+++ b/seleniumbase/__version__.py
@@ -1,2 +1,2 @@
# seleniumbase package
-__version__ = "4.33.2"
+__version__ = "4.33.3"
diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py
index 5d32db78994..54be391799e 100644
--- a/seleniumbase/core/browser_launcher.py
+++ b/seleniumbase/core/browser_launcher.py
@@ -606,6 +606,9 @@ def uc_open_with_cdp_mode(driver, url=None):
cdp.click_nth_element = CDPM.click_nth_element
cdp.click_nth_visible_element = CDPM.click_nth_visible_element
cdp.click_link = CDPM.click_link
+ cdp.go_back = CDPM.go_back
+ cdp.go_forward = CDPM.go_forward
+ cdp.get_navigation_history = CDPM.get_navigation_history
cdp.tile_windows = CDPM.tile_windows
cdp.get_all_cookies = CDPM.get_all_cookies
cdp.set_all_cookies = CDPM.set_all_cookies
@@ -1419,11 +1422,8 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
page_actions.switch_to_window(
driver, driver.current_window_handle, 2, uc_lock=False
)
- if (
- IS_WINDOWS
- and hasattr(pyautogui, "getActiveWindowTitle")
- ):
- py_a_g_title = pyautogui.getActiveWindowTitle()
+ if IS_WINDOWS and hasattr(pyautogui, "getActiveWindowTitle"):
+ py_a_g_title = pyautogui.getActiveWindowTitle() or ""
window_title = driver.get_title()
if not py_a_g_title.startswith(window_title):
window_rect = driver.get_window_rect()
diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py
index 3998c2d6dcf..4318d35916b 100644
--- a/seleniumbase/core/sb_cdp.py
+++ b/seleniumbase/core/sb_cdp.py
@@ -289,6 +289,15 @@ def click_nth_visible_element(self, selector, number):
def click_link(self, link_text):
self.find_elements_by_text(link_text, "a")[0].click()
+ def go_back(self):
+ self.loop.run_until_complete(self.page.back())
+
+ def go_forward(self):
+ self.loop.run_until_complete(self.page.forward())
+
+ def get_navigation_history(self):
+ return self.loop.run_until_complete(self.page.get_navigation_history())
+
def __clear_input(self, element):
return (
self.loop.run_until_complete(element.clear_input_async())
diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py
index 0967fb8d731..5913ec9fad3 100644
--- a/seleniumbase/fixtures/base_case.py
+++ b/seleniumbase/fixtures/base_case.py
@@ -1323,6 +1323,9 @@ def get_locale_code(self):
def go_back(self):
self.__check_scope()
+ if self.__is_cdp_swap_needed():
+ self.cdp.go_back()
+ return
if hasattr(self, "recorder_mode") and self.recorder_mode:
self.save_recorded_actions()
pre_action_url = None
@@ -1348,6 +1351,9 @@ def go_back(self):
def go_forward(self):
self.__check_scope()
+ if self.__is_cdp_swap_needed():
+ self.cdp.go_forward()
+ return
if hasattr(self, "recorder_mode") and self.recorder_mode:
self.save_recorded_actions()
self.__last_page_load_url = None
@@ -1732,6 +1738,9 @@ def click_partial_link_text(self, partial_link_text, timeout=None):
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
partial_link_text = self.__get_type_checked_text(partial_link_text)
+ if self.__is_cdp_swap_needed():
+ self.cdp.find_element(partial_link_text, timeout=timeout).click()
+ return
if not self.is_partial_link_text_present(partial_link_text):
self.wait_for_partial_link_text_present(
partial_link_text, timeout=timeout
@@ -8133,6 +8142,8 @@ def is_connected(self):
def is_chromium(self):
"""Return True if the browser is Chrome or Edge."""
self.__check_scope()
+ if self.__is_cdp_swap_needed():
+ return True
chromium = False
if (
"chrome" in self.driver.capabilities
diff --git a/seleniumbase/undetected/cdp_driver/tab.py b/seleniumbase/undetected/cdp_driver/tab.py
index 96bdbade2b6..7cc3f71c589 100644
--- a/seleniumbase/undetected/cdp_driver/tab.py
+++ b/seleniumbase/undetected/cdp_driver/tab.py
@@ -636,6 +636,10 @@ async def forward(self):
"""History forward"""
await self.send(cdp.runtime.evaluate("window.history.forward()"))
+ async def get_navigation_history(self):
+ """Get Navigation History"""
+ return await self.send(cdp.page.get_navigation_history())
+
async def reload(
self,
ignore_cache: Optional[bool] = True,
diff --git a/setup.py b/setup.py
index 0e72782b069..84927a76d8e 100755
--- a/setup.py
+++ b/setup.py
@@ -193,7 +193,7 @@
'iniconfig==2.0.0',
'pluggy==1.5.0',
"py==1.11.0", # Needed by pytest-html
- 'pytest==8.3.3',
+ 'pytest==8.3.4',
"pytest-html==2.0.1", # Newer ones had issues
'pytest-metadata==3.1.1',
"pytest-ordering==0.6",