Skip to content

Commit c92181b

Browse files
authored
Merge pull request #3216 from seleniumbase/cdp-mode-for-uc-mode-and-more
Add CDP Mode to UC Mode, and more
2 parents e6e3c97 + 52a5822 commit c92181b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+7136
-169
lines changed

README.md

+10-10
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,30 @@
1919
<p align="center">
2020
<a href="#python_installation">🚀 Start</a> |
2121
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/features_list.md">🏰 Features</a> |
22-
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/ReadMe.md">📚 Examples</a> |
2322
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md">🎛️ Options</a> |
23+
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/ReadMe.md">📚 Examples</a> |
2424
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/console_scripts/ReadMe.md">🌠 Scripts</a> |
2525
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/mobile_testing.md">📱 Mobile</a>
2626
<br />
2727
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/method_summary.md">📘 APIs</a> |
28-
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md"> 🔡 Formats</a> |
29-
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/example_logs/ReadMe.md">📊 Dashboard</a> |
28+
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md"> 🔠 Formats</a> |
3029
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/recorder_mode.md">🔴 Recorder</a> |
30+
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/example_logs/ReadMe.md">📊 Dashboard</a> |
3131
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/locale_codes.md">🗾 Locales</a> |
32-
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/utilities/selenium_grid/ReadMe.md">🌐 Grid</a>
32+
<a href="https://seleniumbase.io/devices/?url=seleniumbase.com">💻 Farm</a>
3333
<br />
3434
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/commander.md">🎖️ GUI</a> |
3535
<a href="https://seleniumbase.io/demo_page">📰 TestPage</a> |
36-
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/case_plans.md">🗂️ CasePlans</a> |
3736
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md">👤 UC Mode</a> |
38-
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/master_qa/ReadMe.md">🧬 Hybrid</a> |
39-
<a href="https://seleniumbase.io/devices/?url=seleniumbase.com">💻 Farm</a>
37+
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md">🐙 CDP Mode</a> |
38+
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/chart_maker/ReadMe.md">📶 Charts</a> |
39+
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/utilities/selenium_grid/ReadMe.md">🌐 Grid</a>
4040
<br />
4141
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/how_it_works.md">👁️ How</a> |
4242
<a href="https://github.com/seleniumbase/SeleniumBase/tree/master/examples/migration/raw_selenium">🚝 Migrate</a> |
43-
<a href="https://github.com/seleniumbase/SeleniumBase/tree/master/examples/boilerplates">♻️ Templates</a> |
44-
<a href="https://github.com/seleniumbase/SeleniumBase/tree/master/integrations/node_js">🚉 NodeGUI</a> |
45-
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/chart_maker/ReadMe.md">📶 Charts</a> |
43+
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/case_plans.md">🗂️ CasePlans</a> |
44+
<a href="https://github.com/seleniumbase/SeleniumBase/tree/master/examples/boilerplates">♻️ Template</a> |
45+
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/master_qa/ReadMe.md">🧬 Hybrid</a> |
4646
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/tour_examples/ReadMe.md">🚎 Tours</a>
4747
<br />
4848
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/github/workflows/ReadMe.md">🤖 CI/CD</a> |

examples/cdp_mode/ReadMe.md

+301
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
<!-- SeleniumBase Docs -->
2+
3+
## [<img src="https://seleniumbase.github.io/img/logo6.png" title="SeleniumBase" width="32">](https://github.com/seleniumbase/SeleniumBase/) CDP Mode 🐙
4+
5+
🐙 <b translate="no">SeleniumBase</b> <b translate="no">CDP Mode</b> (Chrome Devtools Protocol Mode) is a special mode inside of <b><a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md" translate="no"><span translate="no">SeleniumBase UC Mode</span></a></b> that lets bots appear human while controlling the browser with the <b translate="no">CDP-Driver</b>. Although regular <span translate="no">UC Mode</span> can't perform <span translate="no">WebDriver</span> actions while the <code>driver</code> is disconnected from the browser, the <span translate="no">CDP-Driver</span> can still perform actions (while maintaining its cover).
6+
7+
👤 <b translate="no">UC Mode</b> avoids bot-detection by first disconnecting WebDriver from the browser at strategic times, calling special <code>PyAutoGUI</code> methods to bypass CAPTCHAs (as needed), and finally reconnecting the <code>driver</code> afterwards so that WebDriver actions can be performed again. Although this approach works for bypassing simple CAPTCHAs, more flexibility is needed for bypassing bot-detection on websites with advanced protection. (That's where <b translate="no">CDP Mode</b> comes in.)
8+
9+
🐙 <b translate="no">CDP Mode</b> is based on <a href="https://github.com/HyperionGray/python-chrome-devtools-protocol" translate="no">python-cdp</a>, <a href="https://github.com/HyperionGray/trio-chrome-devtools-protocol" translate="no">trio-cdp</a>, and <a href="https://github.com/ultrafunkamsterdam/nodriver" translate="no">nodriver</a>. <code>trio-cdp</code> was an early implementation of <code>python-cdp</code>, whereas <code>nodriver</code> is a modern implementation of <code>python-cdp</code>. (Refactored CDP code is imported from <a href="https://github.com/mdmintz/MyCDP" translate="no">MyCDP</a>.)
10+
11+
🐙 <b translate="no">CDP Mode</b> includes multiple updates to the above, such as:
12+
13+
* Sync methods. (Using `async`/`await` is not necessary!)
14+
* The ability to use WebDriver and CDP-Driver together.
15+
* Backwards compatibility for existing UC Mode scripts.
16+
* More configuration options when launching browsers.
17+
* More methods. (And bug-fixes for existing methods.)
18+
* Faster response time for support. (Eg. [Discord Chat](https://discord.gg/EdhQTn3EyE))
19+
20+
--------
21+
22+
### 🐙 <b translate="no">CDP Mode</b> initialization:
23+
24+
* `sb.activate_cdp_mode(url)`
25+
26+
> (Call that from a **UC Mode** script)
27+
28+
--------
29+
30+
### 🐙 <b translate="no">CDP Mode</b> examples:
31+
32+
> [SeleniumBase/examples/cdp_mode](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode)
33+
34+
### 🔖 Example 1: (Pokemon site using Incapsula/Imperva protection with invisible reCAPTCHA)
35+
36+
> [SeleniumBase/examples/cdp_mode/raw_pokemon.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_pokemon.py)
37+
38+
<div></div>
39+
<details>
40+
<summary> ▶️ (<b>Click to expand code preview</b>)</summary>
41+
42+
```python
43+
from seleniumbase import SB
44+
45+
with SB(uc=True, test=True, locale_code="en") as sb:
46+
url = "https://www.pokemon.com/us"
47+
sb.activate_cdp_mode(url)
48+
sb.sleep(1)
49+
sb.cdp.click_if_visible("button#onetrust-reject-all-handler")
50+
sb.cdp.click('a[href="https://www.pokemon.com/us/pokedex/"]')
51+
sb.sleep(1)
52+
sb.cdp.click('b:contains("Show Advanced Search")')
53+
sb.sleep(1)
54+
sb.cdp.click('span[data-type="type"][data-value="electric"]')
55+
sb.cdp.click("a#advSearch")
56+
sb.sleep(1)
57+
sb.cdp.click('img[src*="img/pokedex/detail/025.png"]')
58+
sb.cdp.assert_text("Pikachu", 'div[class*="title"]')
59+
sb.cdp.assert_element('img[alt="Pikachu"]')
60+
sb.cdp.scroll_into_view("div.pokemon-ability-info")
61+
sb.sleep(1)
62+
sb.cdp.flash('div[class*="title"]')
63+
sb.cdp.flash('img[alt="Pikachu"]')
64+
sb.cdp.flash("div.pokemon-ability-info")
65+
name = sb.cdp.get_text("label.styled-select")
66+
info = sb.cdp.get_text("div.version-descriptions p.active")
67+
print("*** %s: ***\n* %s" % (name, info))
68+
sb.sleep(2)
69+
sb.cdp.highlight_overlay("div.pokemon-ability-info")
70+
sb.sleep(2)
71+
sb.cdp.click('a[href="https://www.pokemon.com/us/play-pokemon/"]')
72+
sb.cdp.click('h3:contains("Find an Event")')
73+
location = "Concord, MA, USA"
74+
sb.cdp.type('input[data-testid="location-search"]', location)
75+
sb.sleep(1)
76+
sb.cdp.click("div.autocomplete-dropdown-container div.suggestion-item")
77+
sb.cdp.click('img[alt="search-icon"]')
78+
sb.sleep(2)
79+
events = sb.cdp.select_all('div[data-testid="event-name"]')
80+
print("*** Pokemon events near %s: ***" % location)
81+
for event in events:
82+
print("* " + event.text)
83+
sb.sleep(2)
84+
```
85+
86+
</details>
87+
88+
### 🔖 Example 2: (Hyatt site using Kasada protection)
89+
90+
> [SeleniumBase/examples/cdp_mode/raw_hyatt.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_hyatt.py)
91+
92+
<div></div>
93+
<details>
94+
<summary> ▶️ (<b>Click to expand code preview</b>)</summary>
95+
96+
```python
97+
from seleniumbase import SB
98+
99+
with SB(uc=True, test=True, locale_code="en") as sb:
100+
url = "https://www.hyatt.com/"
101+
sb.activate_cdp_mode(url)
102+
sb.sleep(1)
103+
sb.cdp.click_if_visible('button[aria-label="Close"]')
104+
sb.sleep(0.5)
105+
sb.cdp.click('span:contains("Explore")')
106+
sb.sleep(1)
107+
sb.cdp.click('a:contains("Hotels & Resorts")')
108+
sb.sleep(2.5)
109+
location = "Anaheim, CA, USA"
110+
sb.cdp.press_keys("input#searchbox", location)
111+
sb.sleep(1)
112+
sb.cdp.click("div#suggestion-list ul li a")
113+
sb.sleep(1)
114+
sb.cdp.click('div.hotel-card-footer button')
115+
sb.sleep(1)
116+
sb.cdp.click('button[data-locator="find-hotels"]')
117+
sb.sleep(4)
118+
hotel_names = sb.cdp.select_all(
119+
'div[data-booking-status="BOOKABLE"] [class*="HotelCard_header"]'
120+
)
121+
hotel_prices = sb.cdp.select_all(
122+
'div[data-booking-status="BOOKABLE"] div.rate-currency'
123+
)
124+
sb.assert_true(len(hotel_names) == len(hotel_prices))
125+
print("Hyatt Hotels in %s:" % location)
126+
print("(" + sb.cdp.get_text("ul.b-color_text-white") + ")")
127+
if len(hotel_names) == 0:
128+
print("No availability over the selected dates!")
129+
for i, hotel in enumerate(hotel_names):
130+
print("* %s: %s => %s" % (i + 1, hotel.text, hotel_prices[i].text))
131+
```
132+
133+
</details>
134+
135+
### 🔖 Example 3: (BestWestern site using DataDome protection)
136+
137+
* [SeleniumBase/examples/cdp_mode/raw_bestwestern.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_bestwestern.py)
138+
139+
<div></div>
140+
<details>
141+
<summary> ▶️ (<b>Click to expand code preview</b>)</summary>
142+
143+
```python
144+
from seleniumbase import SB
145+
146+
with SB(uc=True, test=True, locale_code="en") as sb:
147+
url = "https://www.bestwestern.com/en_US.html"
148+
sb.activate_cdp_mode(url)
149+
sb.sleep(1.5)
150+
sb.cdp.click_if_visible("div.onetrust-close-btn-handler")
151+
sb.sleep(0.5)
152+
sb.cdp.click("input#destination-input")
153+
sb.sleep(1.5)
154+
location = "Palm Springs, CA, USA"
155+
sb.cdp.press_keys("input#destination-input", location)
156+
sb.sleep(0.6)
157+
sb.cdp.click("ul#google-suggestions li")
158+
sb.sleep(0.6)
159+
sb.cdp.click("button#btn-modify-stay-update")
160+
sb.sleep(1.5)
161+
sb.cdp.click("label#available-label")
162+
sb.sleep(4)
163+
print("Best Western Hotels in %s:" % location)
164+
summary_details = sb.cdp.get_text("#summary-details-column")
165+
dates = summary_details.split("ROOM")[0].split("DATES")[-1].strip()
166+
print("(Dates: %s)" % dates)
167+
flip_cards = sb.cdp.select_all(".flipCard")
168+
for i, flip_card in enumerate(flip_cards):
169+
hotel = flip_card.query_selector(".hotelName")
170+
price = flip_card.query_selector(".priceSection")
171+
if hotel and price:
172+
print("* %s: %s => %s" % (
173+
i + 1, hotel.text.strip(), price.text.strip())
174+
)
175+
```
176+
177+
</details>
178+
179+
(<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.)
180+
181+
(<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.)
182+
183+
--------
184+
185+
### 🐙 CDP Mode API / Methods
186+
187+
(Some method args have been left out for simplicity. Eg: <code translate="no">timeout</code>)
188+
189+
```python
190+
sb.cdp.get(url)
191+
sb.cdp.reload()
192+
sb.cdp.refresh()
193+
sb.cdp.add_handler(event, handler)
194+
sb.cdp.find_element(selector)
195+
sb.cdp.find_all(selector)
196+
sb.cdp.find_elements_by_text(text, tag_name=None)
197+
sb.cdp.select(selector)
198+
sb.cdp.select_all(selector)
199+
sb.cdp.click_link(link_text)
200+
sb.cdp.tile_windows(windows=None, max_columns=0)
201+
sb.cdp.get_all_cookies(*args, **kwargs)
202+
sb.cdp.set_all_cookies(*args, **kwargs)
203+
sb.cdp.save_cookies(*args, **kwargs)
204+
sb.cdp.load_cookies(*args, **kwargs)
205+
sb.cdp.clear_cookies(*args, **kwargs)
206+
sb.cdp.sleep(seconds)
207+
sb.cdp.bring_active_window_to_front()
208+
sb.cdp.get_active_element()
209+
sb.cdp.get_active_element_css()
210+
sb.cdp.click(selector)
211+
sb.cdp.click_active_element()
212+
sb.cdp.click_if_visible(selector)
213+
sb.cdp.mouse_click(selector)
214+
sb.cdp.nested_click(parent_selector, selector)
215+
sb.cdp.get_nested_element(parent_selector, selector)
216+
sb.cdp.flash(selector)
217+
sb.cdp.focus(selector)
218+
sb.cdp.highlight_overlay(selector)
219+
sb.cdp.remove_element(selector)
220+
sb.cdp.remove_from_dom(selector)
221+
sb.cdp.remove_elements(selector)
222+
sb.cdp.scroll_into_view(selector)
223+
sb.cdp.send_keys(selector, text)
224+
sb.cdp.press_keys(selector, text)
225+
sb.cdp.type(selector, text)
226+
sb.cdp.evaluate(expression)
227+
sb.cdp.js_dumps(obj_name)
228+
sb.cdp.maximize()
229+
sb.cdp.minimize()
230+
sb.cdp.medimize()
231+
sb.cdp.set_window_rect()
232+
sb.cdp.reset_window_size()
233+
sb.cdp.get_window()
234+
sb.cdp.get_text()
235+
sb.cdp.get_title()
236+
sb.cdp.get_current_url()
237+
sb.cdp.get_origin()
238+
sb.cdp.get_page_source()
239+
sb.cdp.get_user_agent()
240+
sb.cdp.get_cookie_string()
241+
sb.cdp.get_locale_code()
242+
sb.cdp.get_screen_rect()
243+
sb.cdp.get_window_rect()
244+
sb.cdp.get_window_size()
245+
sb.cdp.get_window_position()
246+
sb.cdp.get_element_rect(selector)
247+
sb.cdp.get_element_size(selector)
248+
sb.cdp.get_element_position(selector)
249+
sb.cdp.get_gui_element_rect(selector)
250+
sb.cdp.get_gui_element_center(selector)
251+
sb.cdp.get_document()
252+
sb.cdp.get_flattened_document()
253+
sb.cdp.get_element_attributes(selector)
254+
sb.cdp.get_element_html(selector)
255+
sb.cdp.set_attributes(selector, attribute, value)
256+
sb.cdp.internalize_links()
257+
sb.cdp.is_element_present(selector)
258+
sb.cdp.is_element_visible(selector)
259+
sb.cdp.assert_element(selector)
260+
sb.cdp.assert_element_present(selector)
261+
sb.cdp.assert_text(text, selector="html")
262+
sb.cdp.assert_exact_text(text, selector="html")
263+
sb.cdp.save_screenshot(name, folder=None, selector=None)
264+
```
265+
266+
--------
267+
268+
### 🐙 CDP Mode WebElement API / Methods
269+
270+
```python
271+
element.clear_input()
272+
element.click()
273+
element.flash()
274+
element.focus()
275+
element.highlight_overlay()
276+
element.mouse_click()
277+
element.mouse_drag(destination)
278+
element.mouse_move()
279+
element.query_selector(selector)
280+
element.querySelector(selector)
281+
element.query_selector_all(selector)
282+
element.querySelectorAll(selector)
283+
element.remove_from_dom()
284+
element.save_screenshot(*args, **kwargs)
285+
element.save_to_dom()
286+
element.scroll_into_view()
287+
element.select_option()
288+
element.send_file(*file_paths)
289+
element.send_keys(text)
290+
element.set_text(value)
291+
element.type(text)
292+
element.get_position()
293+
element.get_html()
294+
element.get_js_attributes()
295+
```
296+
297+
--------
298+
299+
<img src="https://seleniumbase.github.io/cdn/img/sb_text_f.png" alt="SeleniumBase" title="SeleniumBase" align="center" width="335">
300+
301+
<div><a href="https://github.com/seleniumbase/SeleniumBase"><img src="https://seleniumbase.github.io/cdn/img/sb_logo_gs.png" alt="SeleniumBase" title="SeleniumBase" width="335" /></a></div>

examples/cdp_mode/__init__.py

Whitespace-only changes.

examples/cdp_mode/raw_async.py

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import asyncio
2+
import time
3+
from seleniumbase.core import sb_cdp
4+
from seleniumbase.undetected import cdp_driver
5+
6+
7+
async def main():
8+
driver = await cdp_driver.cdp_util.start()
9+
page = await driver.get("https://www.priceline.com/")
10+
time.sleep(3)
11+
print(await page.evaluate("document.title"))
12+
element = await page.select('[data-testid*="endLocation"]')
13+
await element.click_async()
14+
time.sleep(1)
15+
await element.send_keys_async("Boston")
16+
time.sleep(2)
17+
18+
if __name__ == "__main__":
19+
# Call an async function with awaited methods
20+
loop = asyncio.new_event_loop()
21+
loop.run_until_complete(main())
22+
23+
# Call everything without using async / await
24+
driver = loop.run_until_complete(cdp_driver.cdp_util.start())
25+
page = loop.run_until_complete(driver.get("https://www.pokemon.com/us"))
26+
time.sleep(3)
27+
print(loop.run_until_complete(page.evaluate("document.title")))
28+
element = loop.run_until_complete(page.select("span.icon_pokeball"))
29+
loop.run_until_complete(element.click_async())
30+
time.sleep(1)
31+
print(loop.run_until_complete(page.evaluate("document.title")))
32+
time.sleep(1)
33+
34+
# Call CDP methods via the simplified CDP API
35+
page = loop.run_until_complete(driver.get("https://www.priceline.com/"))
36+
sb = sb_cdp.CDPMethods(loop, page, driver)
37+
sb.sleep(3)
38+
sb.internalize_links() # Don't open links in a new tab
39+
sb.click("#link_header_nav_experiences")
40+
sb.sleep(2)
41+
sb.remove_element("msm-cookie-banner")
42+
sb.sleep(1)
43+
sb.press_keys('input[data-test-id*="search"]', "Amsterdam")
44+
sb.sleep(2)
45+
sb.click('span[data-test-id*="autocomplete"]')
46+
sb.sleep(5)
47+
print(sb.get_title())

0 commit comments

Comments
 (0)