Skip to content

Commit c909b1a

Browse files
authored
Merge pull request #3312 from seleniumbase/cdp-mode-patch-16
CDP Mode - Patch 16
2 parents 667602c + 23a5876 commit c909b1a

File tree

11 files changed

+95
-68
lines changed

11 files changed

+95
-68
lines changed

examples/cdp_mode/ReadMe.md

+36-34
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,30 @@
2222
* Backwards compatibility for existing UC Mode scripts.
2323
* More configuration options when launching browsers.
2424
* More methods. (And bug-fixes for existing methods.)
25+
* `PyAutoGUI` integration for advanced stealth abilities.
2526
* Faster response time for support. (Eg. [Discord Chat](https://discord.gg/EdhQTn3EyE))
2627

2728
--------
2829

29-
### 🐙 <b translate="no">CDP Mode</b> usage:
30+
### 🐙 <b translate="no">CDP Mode</b> Usage:
3031

3132
* **`sb.activate_cdp_mode(url)`**
3233

3334
> (Call that from a **UC Mode** script)
3435
3536
That disconnects WebDriver from Chrome (which prevents detection), and gives you access to `sb.cdp` methods (which don't trigger anti-bot checks).
3637

37-
### 🐙 Here are some common `sb.cdp` methods:
38+
### 🐙 Here are a few common `sb.cdp` methods:
3839

3940
* `sb.cdp.click(selector)`
4041
* `sb.cdp.click_if_visible(selector)`
42+
* `sb.cdp.gui_click_element(selector)`
4143
* `sb.cdp.type(selector, text)`
4244
* `sb.cdp.press_keys(selector, text)`
4345
* `sb.cdp.select_all(selector)`
4446
* `sb.cdp.get_text(selector)`
4547

46-
When `type()` is too fast, use the slower `press_keys()` to avoid detection. You can also use `sb.sleep(seconds)` to slow things down.
48+
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.
4749

4850
To use WebDriver methods again, call:
4951

@@ -63,17 +65,13 @@ To find out if WebDriver is connected or disconnected, call:
6365

6466
--------
6567

66-
### 🐙 <b translate="no">CDP Mode</b> examples:
68+
### 🐙 <b translate="no">CDP Mode</b> Examples ([SeleniumBase/examples/cdp_mode](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode))
6769

68-
> [SeleniumBase/examples/cdp_mode](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode)
69-
70-
### 🔖 Example 1: (Pokemon site using Incapsula/Imperva protection with invisible reCAPTCHA)
71-
72-
> [SeleniumBase/examples/cdp_mode/raw_pokemon.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_pokemon.py)
70+
<p><div /></p>
7371

7472
<div></div>
7573
<details>
76-
<summary> ▶️ (<b>Click to expand code preview</b>)</summary>
74+
<summary> ▶️ 🔖 <b>Example 1: (Pokemon site using Incapsula/Imperva protection with invisible reCAPTCHA)</b></summary>
7775

7876
```python
7977
from seleniumbase import SB
@@ -127,13 +125,12 @@ with SB(uc=True, test=True, locale_code="en", ad_block=True) as sb:
127125

128126
</details>
129127

130-
### 🔖 Example 2: (Hyatt site using Kasada protection)
128+
> [SeleniumBase/examples/cdp_mode/raw_pokemon.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_pokemon.py)
131129
132-
> [SeleniumBase/examples/cdp_mode/raw_hyatt.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_hyatt.py)
133130

134131
<div></div>
135132
<details>
136-
<summary> ▶️ (<b>Click to expand code preview</b>)</summary>
133+
<summary> ▶️ 🔖 <b>Example 2: (Hyatt site using Kasada protection)</b></summary>
137134

138135
```python
139136
from seleniumbase import SB
@@ -157,30 +154,30 @@ with SB(uc=True, test=True, locale_code="en", ad_block=True) as sb:
157154
sb.sleep(1)
158155
sb.cdp.click('button[data-locator="find-hotels"]')
159156
sb.sleep(5)
160-
hotel_names = sb.cdp.select_all(
161-
'div[data-booking-status="BOOKABLE"] [class*="HotelCard_header"]'
162-
)
163-
hotel_prices = sb.cdp.select_all(
164-
'div[data-booking-status="BOOKABLE"] div.rate'
165-
)
166-
sb.assert_true(len(hotel_names) == len(hotel_prices))
157+
card_info = 'div[data-booking-status="BOOKABLE"] [class*="HotelCard_info"]'
158+
hotels = sb.cdp.select_all(card_info)
167159
print("Hyatt Hotels in %s:" % location)
168160
print("(" + sb.cdp.get_text("ul.b-color_text-white") + ")")
169-
if len(hotel_names) == 0:
161+
if len(hotels) == 0:
170162
print("No availability over the selected dates!")
171-
for i, hotel in enumerate(hotel_names):
172-
print("* %s: %s => %s" % (i + 1, hotel.text, hotel_prices[i].text))
163+
for hotel in hotels:
164+
info = hotel.text.strip()
165+
if "Avg/Night" in info and not info.startswith("Rates from"):
166+
name = info.split(" (")[0].split(" + ")[0].split(" Award Cat")[0]
167+
price = "?"
168+
if "Rates from : " in info:
169+
price = info.split("Rates from : ")[1].split(" Avg/Night")[0]
170+
print("* %s => %s" % (name, price))
173171
```
174172

175173
</details>
176174

177-
### 🔖 Example 3: (BestWestern site using DataDome protection)
175+
> [SeleniumBase/examples/cdp_mode/raw_hyatt.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_hyatt.py)
178176
179-
* [SeleniumBase/examples/cdp_mode/raw_bestwestern.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_bestwestern.py)
180177

181178
<div></div>
182179
<details>
183-
<summary> ▶️ (<b>Click to expand code preview</b>)</summary>
180+
<summary> ▶️ 🔖 <b>Example 3: (BestWestern site using DataDome protection)</b></summary>
184181

185182
```python
186183
from seleniumbase import SB
@@ -218,13 +215,12 @@ with SB(uc=True, test=True, locale_code="en", ad_block=True) as sb:
218215

219216
</details>
220217

221-
### 🔖 Example 4: (Walmart site using Akamai protection with PerimeterX)
218+
> [SeleniumBase/examples/cdp_mode/raw_bestwestern.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_bestwestern.py)
222219
223-
* [SeleniumBase/examples/cdp_mode/raw_walmart.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_walmart.py)
224220

225221
<div></div>
226222
<details>
227-
<summary> ▶️ (<b>Click to expand code preview</b>)</summary>
223+
<summary> ▶️ 🔖 <b>Example 4: (Walmart site using Akamai protection with PerimeterX)</b></summary>
228224

229225
```python
230226
from seleniumbase import SB
@@ -264,13 +260,12 @@ with SB(uc=True, test=True, locale_code="en", ad_block=True) as sb:
264260

265261
</details>
266262

267-
### 🔖 Example 5: (Nike site using Shape Security)
263+
> [SeleniumBase/examples/cdp_mode/raw_walmart.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_walmart.py)
268264
269-
* [SeleniumBase/examples/cdp_mode/raw_nike.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_nike.py)
270265

271266
<div></div>
272267
<details>
273-
<summary> ▶️ (<b>Click to expand code preview</b>)</summary>
268+
<summary> ▶️ 🔖 <b>Example 5: (Nike site using Shape Security)</b></summary>
274269

275270
```python
276271
from seleniumbase import SB
@@ -294,13 +289,17 @@ with SB(uc=True, test=True, locale_code="en", ad_block=True) as sb:
294289

295290
</details>
296291

292+
> [SeleniumBase/examples/cdp_mode/raw_nike.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_nike.py)
293+
294+
<p><div /></p>
295+
297296
(<b>Note:</b> Extra <code translate="no">sb.sleep()</code> calls have been added to prevent bot-detection because some sites will flag you as a bot if you perform actions too quickly.)
298297

299298
(<b>Note:</b> Some sites may IP-block you for 36 hours or more if they catch you using regular <span translate="no">Selenium WebDriver</span>. Be extra careful when creating and/or modifying automation scripts that run on them.)
300299

301300
--------
302301

303-
### 🐙 CDP Mode API / Methods
302+
### 🐙 <b translate="no">CDP Mode</b> API / Methods
304303

305304
(Some method args have been left out for simplicity. Eg: <code translate="no">timeout</code>)
306305

@@ -323,6 +322,9 @@ sb.cdp.find_visible_elements(selector)
323322
sb.cdp.click_nth_element(selector, number)
324323
sb.cdp.click_nth_visible_element(selector, number)
325324
sb.cdp.click_link(link_text)
325+
sb.cdp.go_back()
326+
sb.cdp.go_forward()
327+
sb.cdp.get_navigation_history()
326328
sb.cdp.tile_windows(windows=None, max_columns=0)
327329
sb.cdp.get_all_cookies(*args, **kwargs)
328330
sb.cdp.set_all_cookies(*args, **kwargs)
@@ -434,7 +436,7 @@ sb.cdp.save_screenshot(name, folder=None, selector=None)
434436

435437
--------
436438

437-
### 🐙 CDP Mode WebElement API / Methods
439+
### 🐙 <b translate="no">CDP Mode</b> WebElement API / Methods
438440

439441
```python
440442
element.clear_input()

examples/cdp_mode/raw_hyatt.py

+11-10
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,17 @@
1919
sb.sleep(1)
2020
sb.cdp.click('button[data-locator="find-hotels"]')
2121
sb.sleep(5)
22-
hotel_names = sb.cdp.select_all(
23-
'div[data-booking-status="BOOKABLE"] [class*="HotelCard_header"]'
24-
)
25-
hotel_prices = sb.cdp.select_all(
26-
'div[data-booking-status="BOOKABLE"] div.rate'
27-
)
28-
sb.assert_true(len(hotel_names) == len(hotel_prices))
22+
card_info = 'div[data-booking-status="BOOKABLE"] [class*="HotelCard_info"]'
23+
hotels = sb.cdp.select_all(card_info)
2924
print("Hyatt Hotels in %s:" % location)
3025
print("(" + sb.cdp.get_text("ul.b-color_text-white") + ")")
31-
if len(hotel_names) == 0:
26+
if len(hotels) == 0:
3227
print("No availability over the selected dates!")
33-
for i, hotel in enumerate(hotel_names):
34-
print("* %s: %s => %s" % (i + 1, hotel.text, hotel_prices[i].text))
28+
for hotel in hotels:
29+
info = hotel.text.strip()
30+
if "Avg/Night" in info and not info.startswith("Rates from"):
31+
name = info.split(" (")[0].split(" + ")[0].split(" Award Cat")[0]
32+
price = "?"
33+
if "Rates from : " in info:
34+
price = info.split("Rates from : ")[1].split(" Avg/Night")[0]
35+
print("* %s => %s" % (name, price))

examples/presenter/uc_presentation_4.py

+15-15
Original file line numberDiff line numberDiff line change
@@ -695,24 +695,24 @@ def test_presentation_4(self):
695695
sb.sleep(1)
696696
sb.cdp.click('button[data-locator="find-hotels"]')
697697
sb.sleep(5)
698-
hotel_names = sb.cdp.select_all(
699-
'div[data-booking-status="BOOKABLE"]'
700-
' [class*="HotelCard_header"]'
698+
card_info = (
699+
'div[data-booking-status="BOOKABLE"] [class*="HotelCard_info"]'
701700
)
702-
hotel_prices = sb.cdp.select_all(
703-
'div[data-booking-status="BOOKABLE"] div.rate'
704-
)
705-
sb.assert_true(len(hotel_names) == len(hotel_prices))
706-
print("\n\nHyatt Hotels in %s:" % location)
701+
hotels = sb.cdp.select_all(card_info)
702+
print("Hyatt Hotels in %s:" % location)
707703
print("(" + sb.cdp.get_text("ul.b-color_text-white") + ")")
708-
if len(hotel_names) == 0:
704+
if len(hotels) == 0:
709705
print("No availability over the selected dates!")
710-
for i, hotel in enumerate(hotel_names):
711-
with suppress(Exception):
712-
print(
713-
"* %s: %s => %s"
714-
% (i + 1, hotel.text, hotel_prices[i].text)
715-
)
706+
for hotel in hotels:
707+
info = hotel.text.strip()
708+
if "Avg/Night" in info and not info.startswith("Rates from"):
709+
name = info.split(" (")[0]
710+
name = name.split(" + ")[0].split(" Award Cat")[0]
711+
price = "?"
712+
if "Rates from : " in info:
713+
price = info.split("Rates from : ")[1]
714+
price = price.split(" Avg/Night")[0]
715+
print("* %s => %s" % (name, price))
716716

717717
self.create_presentation(theme="serif", transition="none")
718718
self.add_slide(

mkdocs_build/requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ lxml==5.3.0
2020
pyquery==2.0.1
2121
readtime==3.0.0
2222
mkdocs==1.6.1
23-
mkdocs-material==9.5.46
23+
mkdocs-material==9.5.47
2424
mkdocs-exclude-search==0.6.6
2525
mkdocs-simple-hooks==0.1.5
2626
mkdocs-material-extensions==1.3.1

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ execnet==2.1.1
4444
iniconfig==2.0.0
4545
pluggy==1.5.0
4646
py==1.11.0
47-
pytest==8.3.3
47+
pytest==8.3.4
4848
pytest-html==2.0.1
4949
pytest-metadata==3.1.1
5050
pytest-ordering==0.6

seleniumbase/__version__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "4.33.2"
2+
__version__ = "4.33.3"

seleniumbase/core/browser_launcher.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,9 @@ def uc_open_with_cdp_mode(driver, url=None):
606606
cdp.click_nth_element = CDPM.click_nth_element
607607
cdp.click_nth_visible_element = CDPM.click_nth_visible_element
608608
cdp.click_link = CDPM.click_link
609+
cdp.go_back = CDPM.go_back
610+
cdp.go_forward = CDPM.go_forward
611+
cdp.get_navigation_history = CDPM.get_navigation_history
609612
cdp.tile_windows = CDPM.tile_windows
610613
cdp.get_all_cookies = CDPM.get_all_cookies
611614
cdp.set_all_cookies = CDPM.set_all_cookies
@@ -1419,11 +1422,8 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
14191422
page_actions.switch_to_window(
14201423
driver, driver.current_window_handle, 2, uc_lock=False
14211424
)
1422-
if (
1423-
IS_WINDOWS
1424-
and hasattr(pyautogui, "getActiveWindowTitle")
1425-
):
1426-
py_a_g_title = pyautogui.getActiveWindowTitle()
1425+
if IS_WINDOWS and hasattr(pyautogui, "getActiveWindowTitle"):
1426+
py_a_g_title = pyautogui.getActiveWindowTitle() or ""
14271427
window_title = driver.get_title()
14281428
if not py_a_g_title.startswith(window_title):
14291429
window_rect = driver.get_window_rect()

seleniumbase/core/sb_cdp.py

+9
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,15 @@ def click_nth_visible_element(self, selector, number):
289289
def click_link(self, link_text):
290290
self.find_elements_by_text(link_text, "a")[0].click()
291291

292+
def go_back(self):
293+
self.loop.run_until_complete(self.page.back())
294+
295+
def go_forward(self):
296+
self.loop.run_until_complete(self.page.forward())
297+
298+
def get_navigation_history(self):
299+
return self.loop.run_until_complete(self.page.get_navigation_history())
300+
292301
def __clear_input(self, element):
293302
return (
294303
self.loop.run_until_complete(element.clear_input_async())

seleniumbase/fixtures/base_case.py

+11
Original file line numberDiff line numberDiff line change
@@ -1323,6 +1323,9 @@ def get_locale_code(self):
13231323

13241324
def go_back(self):
13251325
self.__check_scope()
1326+
if self.__is_cdp_swap_needed():
1327+
self.cdp.go_back()
1328+
return
13261329
if hasattr(self, "recorder_mode") and self.recorder_mode:
13271330
self.save_recorded_actions()
13281331
pre_action_url = None
@@ -1348,6 +1351,9 @@ def go_back(self):
13481351

13491352
def go_forward(self):
13501353
self.__check_scope()
1354+
if self.__is_cdp_swap_needed():
1355+
self.cdp.go_forward()
1356+
return
13511357
if hasattr(self, "recorder_mode") and self.recorder_mode:
13521358
self.save_recorded_actions()
13531359
self.__last_page_load_url = None
@@ -1732,6 +1738,9 @@ def click_partial_link_text(self, partial_link_text, timeout=None):
17321738
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
17331739
timeout = self.__get_new_timeout(timeout)
17341740
partial_link_text = self.__get_type_checked_text(partial_link_text)
1741+
if self.__is_cdp_swap_needed():
1742+
self.cdp.find_element(partial_link_text, timeout=timeout).click()
1743+
return
17351744
if not self.is_partial_link_text_present(partial_link_text):
17361745
self.wait_for_partial_link_text_present(
17371746
partial_link_text, timeout=timeout
@@ -8133,6 +8142,8 @@ def is_connected(self):
81338142
def is_chromium(self):
81348143
"""Return True if the browser is Chrome or Edge."""
81358144
self.__check_scope()
8145+
if self.__is_cdp_swap_needed():
8146+
return True
81368147
chromium = False
81378148
if (
81388149
"chrome" in self.driver.capabilities

seleniumbase/undetected/cdp_driver/tab.py

+4
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,10 @@ async def forward(self):
636636
"""History forward"""
637637
await self.send(cdp.runtime.evaluate("window.history.forward()"))
638638

639+
async def get_navigation_history(self):
640+
"""Get Navigation History"""
641+
return await self.send(cdp.page.get_navigation_history())
642+
639643
async def reload(
640644
self,
641645
ignore_cache: Optional[bool] = True,

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@
193193
'iniconfig==2.0.0',
194194
'pluggy==1.5.0',
195195
"py==1.11.0", # Needed by pytest-html
196-
'pytest==8.3.3',
196+
'pytest==8.3.4',
197197
"pytest-html==2.0.1", # Newer ones had issues
198198
'pytest-metadata==3.1.1',
199199
"pytest-ordering==0.6",

0 commit comments

Comments
 (0)