Skip to content

Commit b991086

Browse files
authored
Merge pull request #3405 from seleniumbase/cdp-mode-patch-23
CDP Mode - Patch 23
2 parents 11e81b0 + 4ef0812 commit b991086

File tree

12 files changed

+240
-19
lines changed

12 files changed

+240
-19
lines changed

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2014-2024 Michael Mintz
3+
Copyright (c) 2014-2025 Michael Mintz
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

examples/cdp_mode/ReadMe.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66

77
--------
88

9-
<!-- YouTube View --><a href="https://www.youtube.com/watch?v=Mr90iQmNsKM"><img src="http://img.youtube.com/vi/Mr90iQmNsKM/0.jpg" title="SeleniumBase on YouTube" width="366" /></a>
9+
<!-- YouTube View --><a href="https://www.youtube.com/watch?v=Mr90iQmNsKM"><img src="https://github.com/user-attachments/assets/91e7ff7b-d155-4ba9-b17b-b097825fcf42" title="SeleniumBase on YouTube" width="350" /></a>
1010
<p>(<b><a href="https://www.youtube.com/watch?v=Mr90iQmNsKM">Watch the CDP Mode tutorial on YouTube! ▶️</a></b>)</p>
1111

1212
--------
1313

14+
<!-- YouTube View --><a href="https://www.youtube.com/watch?v=vt2zsdiNh3U"><img src="https://github.com/user-attachments/assets/82ab2715-727e-4d09-9314-b8905795dc43" title="SeleniumBase on YouTube" width="350" /></a>
15+
<p>(<b><a href="https://www.youtube.com/watch?v=vt2zsdiNh3U">Watch "Hacking websites with CDP" on YouTube! ▶️</a></b>)</p>
16+
17+
--------
18+
1419
👤 <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.)
1520

1621
🐙 <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> is an early implementation of <code>python-cdp</code>, and <code>nodriver</code> is a modern implementation of <code>python-cdp</code>. (Refactored <code>Python-CDP</code> code is imported from <a href="https://github.com/mdmintz/MyCDP" translate="no">MyCDP</a>.)
+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# https://www.youtube.com/watch?v=vt2zsdiNh3U
2+
from seleniumbase import BaseCase
3+
BaseCase.main(__name__, __file__)
4+
5+
6+
class UCPresentationClass(BaseCase):
7+
def test_hacking_with_cdp(self):
8+
self.open("data:,")
9+
self.set_window_position(4, 40)
10+
self._output_file_saves = False
11+
self.create_presentation(theme="serif", transition="none")
12+
self.add_slide("<h2>Press SPACE to begin!</h2>\n")
13+
self.add_slide(
14+
"<p><h3><mk-0>Coming up on the Hacker Show...</mk-0></h3></p>\n"
15+
"<hr /><ul>\n"
16+
'<img src="https://seleniumbase.io/other/hackers_at_comp.jpg"'
17+
' width="100%">'
18+
"</ul>",
19+
)
20+
self.add_slide(
21+
"<p><b>Coming up on the Hacker Show...</b></p>\n"
22+
"<hr /><br /><ul>\n"
23+
"<li><mk-0>Intercepting requests/responses/XHR with CDP."
24+
"</mk-0></li><br />\n"
25+
"<br /><br />\n"
26+
"<br /><br />\n"
27+
"<br /><br />\n"
28+
"<br /><br />\n"
29+
"</ul>",
30+
)
31+
self.add_slide(
32+
"<p><b>Coming up on the Hacker Show...</b></p>\n"
33+
"<hr /><br /><ul>\n"
34+
"<li>Intercepting requests/responses/XHR with CDP."
35+
"</li><br />\n"
36+
"<li><mk-0>Modifying requests: CDP.Fetch.continueRequest."
37+
"</mk-0></li><br />\n"
38+
"<br /><br />\n"
39+
"<br /><br />\n"
40+
"<br /><br />\n"
41+
"</ul>",
42+
)
43+
self.add_slide(
44+
"<p><b>Coming up on the Hacker Show...</b></p>\n"
45+
"<hr /><br /><ul>\n"
46+
"<li>Intercepting requests/responses/XHR with CDP."
47+
"</li><br />\n"
48+
"<li>Modifying requests: CDP.Fetch.continueRequest."
49+
"</li><br />\n"
50+
"<li><mk-0>Controlling browsers via remote-debugging-port"
51+
"</mk-0></li><br />\n"
52+
"<br /><br />\n"
53+
"<br /><br />\n"
54+
"</ul>",
55+
)
56+
self.add_slide(
57+
"<p><b>Coming up on the Hacker Show...</b></p>\n"
58+
"<hr /><br /><ul>\n"
59+
"<li>Intercepting requests/responses/XHR with CDP."
60+
"</li><br />\n"
61+
"<li>Modifying requests: CDP.Fetch.continueRequest."
62+
"</li><br />\n"
63+
"<li>Controlling browsers via remote-debugging-port"
64+
"</li><br />\n"
65+
"<li><mk-0>Bypassing CAPTCHAs & anti-bot defenses."
66+
"</mk-0></li><br />\n"
67+
"<br /><br />\n"
68+
"</ul>",
69+
)
70+
self.add_slide(
71+
"<p><b>Coming up on the Hacker Show...</b></p>\n"
72+
"<hr /><br /><ul>\n"
73+
"<li>Intercepting requests/responses/XHR with CDP."
74+
"</li><br />\n"
75+
"<li>Modifying requests: CDP.Fetch.continueRequest."
76+
"</li><br />\n"
77+
"<li>Controlling browsers via remote-debugging-port"
78+
"</li><br />\n"
79+
"<li>Bypassing CAPTCHAs & anti-bot defenses."
80+
"</li><br />\n"
81+
"<li><mk-0>And live demos of all the above... with Python!"
82+
"</mk-0></li><br />\n"
83+
"</ul>",
84+
)
85+
self.add_slide(
86+
"<h2>Get ready for some<br />serious hacking!</h2>"
87+
)
88+
self.add_slide(
89+
'<img src="https://seleniumbase.io/other/hacking_with_cdp.jpg"'
90+
' width="100%">'
91+
)
92+
self.add_slide(
93+
'<img src="https://seleniumbase.io/other/cdp.jpg"'
94+
' width="100%">'
95+
)
96+
self.add_slide(
97+
'<img src="https://seleniumbase.io/other/ms_edp.jpg"'
98+
' width="100%">'
99+
)
100+
self.add_slide(
101+
'<img src="https://seleniumbase.io/other/vid4_on_yt.jpg"'
102+
' width="100%">'
103+
)
104+
self.add_slide(
105+
'<img src="https://seleniumbase.io/other/cdp_in_sb.jpg"'
106+
' width="100%">'
107+
)
108+
self.add_slide(
109+
'<img src="https://seleniumbase.io/other/hacker_news.png"'
110+
' width="100%">'
111+
)
112+
self.add_slide(
113+
'<img src="https://seleniumbase.io/other/sb_star_history_3.png"'
114+
' width="100%">'
115+
)
116+
self.add_slide(
117+
'<img src="https://seleniumbase.io/other/top_trending_month.png"'
118+
' width="100%">'
119+
)
120+
self.add_slide(
121+
'<img src="https://seleniumbase.io/other/cdp_con_req.jpg"'
122+
' width="100%">'
123+
)
124+
self.add_slide(
125+
'<img src="https://seleniumbase.io/other/mycdp_con_req.jpg"'
126+
' width="100%">'
127+
)
128+
self.add_slide(
129+
'<img src="https://seleniumbase.io/other/sb_con_req.jpg"'
130+
' width="100%">'
131+
)
132+
self.add_slide(
133+
'<img src="https://seleniumbase.io/other/xhr_info.jpg"'
134+
' width="100%">'
135+
)
136+
self.add_slide(
137+
'<h3>The <code>remote-debugging-port</code></h3>'
138+
'<img src="https://seleniumbase.io/other/rd_port.jpg"'
139+
' width="100%">'
140+
)
141+
self.add_slide(
142+
"<h3>Let's get to the fun part...</h3>"
143+
'<img src="https://seleniumbase.io/other/hackers_at_comp.jpg"'
144+
' width="80%">'
145+
)
146+
self.begin_presentation(filename="uc_presentation.html")

examples/raw_skype_mobile.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""Mobile emulation test for Skype."""
2+
from seleniumbase import SB
3+
4+
with SB(mobile=True, test=True) as sb:
5+
sb.open("https://www.skype.com/en/get-skype/")
6+
sb.assert_element('[aria-label="Microsoft"]')
7+
sb.assert_text("Download Skype", "h1")
8+
sb.highlight("div.appBannerContent")
9+
sb.highlight("h1")
10+
sb.assert_text("Skype for Mobile", "h2")
11+
sb.highlight("h2")
12+
sb.highlight("#get-skype-0")
13+
sb.highlight_click("span[data-dropdown-icon]")
14+
sb.highlight("#get-skype-0_android-download")
15+
sb.highlight('[data-bi-id*="ios"]')

mkdocs_build/requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Minimum Python version: 3.9 (for generating docs only)
33

44
regex>=2024.11.6
5-
pymdown-extensions>=10.13
5+
pymdown-extensions>=10.14
66
pipdeptree>=2.24.0
77
python-dateutil>=2.8.2
88
Markdown==3.7

requirements.txt

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pip>=24.3.1
22
packaging>=24.2
33
setuptools~=70.2;python_version<"3.10"
4-
setuptools>=75.6.0;python_version>="3.10"
4+
setuptools>=75.8.0;python_version>="3.10"
55
wheel>=0.45.1
66
attrs>=24.3.0
77
certifi>=2024.12.14
@@ -23,7 +23,7 @@ parse>=1.20.2
2323
parse-type>=0.6.4
2424
colorama>=0.4.6
2525
pyyaml>=6.0.2
26-
pygments>=2.18.0
26+
pygments>=2.19.1
2727
pyreadline3>=3.5.3;platform_system=="Windows"
2828
tabcompleter>=1.4.0
2929
pdbp>=1.6.1
@@ -36,7 +36,8 @@ requests==2.32.3
3636
sniffio==1.3.1
3737
h11==0.14.0
3838
outcome==1.3.0.post0
39-
trio==0.27.0
39+
trio==0.27.0;python_version<"3.9"
40+
trio==0.28.0;python_version>="3.9"
4041
trio-websocket==0.11.1
4142
wsproto==1.2.0
4243
websocket-client==1.8.0
@@ -67,7 +68,7 @@ rich==13.9.4
6768
# ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.)
6869

6970
coverage>=7.6.1;python_version<"3.9"
70-
coverage>=7.6.9;python_version>="3.9"
71+
coverage>=7.6.10;python_version>="3.9"
7172
pytest-cov>=5.0.0;python_version<"3.9"
7273
pytest-cov>=6.0.0;python_version>="3.9"
7374
flake8==5.0.4;python_version<"3.9"

seleniumbase/__version__.py

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

seleniumbase/core/browser_launcher.py

+21-3
Original file line numberDiff line numberDiff line change
@@ -533,10 +533,26 @@ def uc_open_with_cdp_mode(driver, url=None):
533533
if url_protocol not in ["about", "data", "chrome"]:
534534
safe_url = False
535535

536+
headless = False
537+
headed = None
538+
xvfb = None
539+
if hasattr(sb_config, "headless"):
540+
headless = sb_config.headless
541+
if hasattr(sb_config, "headed"):
542+
headed = sb_config.headed
543+
if hasattr(sb_config, "xvfb"):
544+
xvfb = sb_config.xvfb
545+
536546
loop = asyncio.new_event_loop()
537547
asyncio.set_event_loop(loop)
538548
driver.cdp_base = loop.run_until_complete(
539-
cdp_util.start(host=cdp_host, port=cdp_port)
549+
cdp_util.start(
550+
host=cdp_host,
551+
port=cdp_port,
552+
headless=headless,
553+
headed=headed,
554+
xvfb=xvfb,
555+
)
540556
)
541557
loop.run_until_complete(driver.cdp_base.wait(0))
542558

@@ -863,13 +879,15 @@ def __install_pyautogui_if_missing():
863879
xvfb_height = 768
864880
sb_config._xvfb_height = xvfb_height
865881
with suppress(Exception):
866-
xvfb_display = Display(
882+
_xvfb_display = Display(
867883
visible=True,
868884
size=(xvfb_width, xvfb_height),
869885
backend="xvfb",
870886
use_xauth=True,
871887
)
872-
xvfb_display.start()
888+
_xvfb_display.start()
889+
sb_config._virtual_display = _xvfb_display
890+
sb_config.headless_active = True
873891

874892

875893
def install_pyautogui_if_missing(driver):

seleniumbase/fixtures/base_case.py

+22-3
Original file line numberDiff line numberDiff line change
@@ -13795,7 +13795,8 @@ def __switch_to_newest_window_if_not_blank(self):
1379513795
if self.get_current_url() == "about:blank":
1379613796
self.switch_to_window(current_window)
1379713797
except Exception:
13798-
self.switch_to_window(current_window)
13798+
with suppress(Exception):
13799+
self.switch_to_window(current_window)
1379913800

1380013801
def __needs_minimum_wait(self):
1380113802
if (
@@ -14004,9 +14005,10 @@ def __activate_standard_virtual_display(self):
1400414005
visible=0, size=(width, height)
1400514006
)
1400614007
self._xvfb_display.start()
14007-
sb_config._virtual_display = self._xvfb_display
1400814008
self.headless_active = True
14009-
sb_config.headless_active = True
14009+
if not self.undetectable:
14010+
sb_config._virtual_display = self._xvfb_display
14011+
sb_config.headless_active = True
1401014012

1401114013
def __activate_virtual_display(self):
1401214014
if self.undetectable and not (self.headless or self.headless2):
@@ -14029,6 +14031,8 @@ def __activate_virtual_display(self):
1402914031
"\nX11 display failed! Will use regular xvfb!"
1403014032
)
1403114033
self.__activate_standard_virtual_display()
14034+
else:
14035+
self.headless_active = True
1403214036
except Exception as e:
1403314037
if hasattr(e, "msg"):
1403414038
print("\n" + str(e.msg))
@@ -16601,6 +16605,7 @@ def tearDown(self):
1660116605
self.__quit_all_drivers()
1660216606
# Resume tearDown() for all test runners, (Pytest / Pynose / Behave)
1660316607
if hasattr(self, "_xvfb_display") and self._xvfb_display:
16608+
# Stop the Xvfb virtual display launched from BaseCase
1660416609
try:
1660516610
if hasattr(self._xvfb_display, "stop"):
1660616611
self._xvfb_display.stop()
@@ -16610,6 +16615,20 @@ def tearDown(self):
1661016615
pass
1661116616
except Exception:
1661216617
pass
16618+
if (
16619+
hasattr(sb_config, "_virtual_display")
16620+
and sb_config._virtual_display
16621+
and hasattr(sb_config._virtual_display, "stop")
16622+
):
16623+
# CDP Mode may launch a 2nd Xvfb virtual display
16624+
try:
16625+
sb_config._virtual_display.stop()
16626+
sb_config._virtual_display = None
16627+
sb_config.headless_active = False
16628+
except AttributeError:
16629+
pass
16630+
except Exception:
16631+
pass
1661316632
if self.__visual_baseline_copies:
1661416633
sb_config._visual_baseline_copies = True
1661516634
if has_exception:

seleniumbase/plugins/sb_manager.py

+13
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,19 @@ def SB(
12561256
print(traceback.format_exc().strip())
12571257
if test and not test_passed:
12581258
print("********** ERROR: The test AND the tearDown() FAILED!")
1259+
if (
1260+
hasattr(sb_config, "_virtual_display")
1261+
and sb_config._virtual_display
1262+
and hasattr(sb_config._virtual_display, "stop")
1263+
):
1264+
try:
1265+
sb_config._virtual_display.stop()
1266+
sb_config._virtual_display = None
1267+
sb_config.headless_active = False
1268+
except AttributeError:
1269+
pass
1270+
except Exception:
1271+
pass
12591272
end_time = time.time()
12601273
run_time = end_time - start_time
12611274
sb_config = sb_config_backup

seleniumbase/undetected/cdp_driver/cdp_util.py

+3
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ def __activate_virtual_display_as_needed(
8484
"\nX11 display failed! Will use regular xvfb!"
8585
)
8686
__activate_standard_virtual_display()
87+
else:
88+
sb_config._virtual_display = _xvfb_display
89+
sb_config.headless_active = True
8790
except Exception as e:
8891
if hasattr(e, "msg"):
8992
print("\n" + str(e.msg))

0 commit comments

Comments
 (0)