Skip to content

Commit 58a3516

Browse files
authored
Merge pull request #4286 from seleniumbase/cdp-mode-patch-98
CDP Mode: Patch 98
2 parents dd4b1d3 + 951a894 commit 58a3516

File tree

8 files changed

+57
-37
lines changed

8 files changed

+57
-37
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
<h1>SeleniumBase</h1>
1111

12-
<p align="center"><a href="https://github.com/seleniumbase/SeleniumBase/"><img src="https://seleniumbase.github.io/other/sbase_text_logo3t.png" alt="SeleniumBase" title="SeleniumBase" width="630" /></a></p>
12+
<p align="center"><a href="https://github.com/seleniumbase/SeleniumBase/"><img src="https://seleniumbase.github.io/other/sbase_text_logo3t.png" alt="SeleniumBase" title="SeleniumBase" width="634" /></a></p>
1313

1414
<p align="center" class="hero__title"><b>All-in-one Browser Automation Framework:<br />Web Crawling / Testing / Scraping / Stealth</b></p>
1515

@@ -54,11 +54,11 @@
5454

5555
📊 <a href="https://github.com/seleniumbase/SeleniumBase/"><b translate="no">SeleniumBase</b></a> is a complete framework for web automation, testing, scraping, and stealth. Includes a <a href="https://docs.pytest.org/en/latest/how-to/usage.html">pytest</a> integration for customizing automation from the command-line.
5656

57-
👤 Stealth modes: <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md">UC Mode</a> and <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md"><b>CDP Mode</b></a> can bypass bot-detection, solve CAPTCHAs, and call methods from the <a href="https://chromedevtools.github.io/devtools-protocol/" translate="no">Chrome Devtools Protocol</a>. Includes <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/playwright/ReadMe.md"><b><span translate="no">Stealthy Playwright Mode</span></b></a>, which makes Playwright stealthy via CDP Mode.
57+
🐙 Stealth modes: <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md">UC Mode</a> and <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md"><b>CDP Mode</b></a> can bypass bot-detection, solve CAPTCHAs, and call methods from the <a href="https://chromedevtools.github.io/devtools-protocol/" translate="no">Chrome Devtools Protocol</a>. Includes <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/playwright/ReadMe.md"><b><span translate="no">Stealthy Playwright Mode</span></b></a>, which makes Playwright stealthy via CDP Mode.
5858

5959
📚 Example scripts and tests are located in [**SeleniumBase/examples/**](https://github.com/seleniumbase/SeleniumBase/tree/master/examples).
6060

61-
👤 Stealthy example scripts are located in [**SeleniumBase/examples/cdp_mode/**](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode).
61+
🥷 Stealthy example scripts are located in [**SeleniumBase/examples/cdp_mode/**](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode).
6262

6363
--------
6464

examples/cdp_mode/raw_stopandshop.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Test Stop & Shop search. Non-US IPs might be blocked."""
22
from seleniumbase import SB
33

4-
with SB(uc=True, test=True, ad_block=True) as sb:
4+
with SB(uc=True, test=True, incognito=True) as sb:
55
url = "https://stopandshop.com/"
66
sb.activate_cdp_mode(url)
77
sb.sleep(2.6)

requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ websockets>=16.0;python_version>="3.10"
1111
filelock~=3.19.1;python_version<"3.10"
1212
filelock>=3.25.2;python_version>="3.10"
1313
fasteners>=0.20
14-
mycdp>=1.3.4
14+
mycdp>=1.3.6
1515
pynose>=1.5.5
1616
platformdirs~=4.4.0;python_version<"3.10"
1717
platformdirs>=4.9.4;python_version>="3.10"
@@ -29,7 +29,7 @@ pyreadline3>=3.5.4;platform_system=="Windows"
2929
tabcompleter>=1.4.0
3030
pdbp>=1.8.2
3131
idna>=3.11
32-
charset-normalizer>=3.4.5,<4
32+
charset-normalizer>=3.4.6,<4
3333
urllib3>=1.26.20,<2;python_version<"3.10"
3434
urllib3>=1.26.20,<3;python_version>="3.10"
3535
requests~=2.32.5
@@ -77,7 +77,7 @@ rich>=14.3.3,<15
7777
# ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.)
7878

7979
coverage>=7.10.7;python_version<"3.10"
80-
coverage>=7.13.4;python_version>="3.10"
80+
coverage>=7.13.5;python_version>="3.10"
8181
pytest-cov>=7.0.0
8282
flake8==7.3.0
8383
mccabe==0.7.0

seleniumbase/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "4.47.3"
2+
__version__ = "4.47.4"

seleniumbase/core/sb_cdp.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ def select(self, selector, timeout=None):
391391

392392
def select_all(self, selector, timeout=None):
393393
if not timeout:
394-
timeout = settings.SMALL_TIMEOUT
394+
timeout = settings.MINI_TIMEOUT
395395
self.__add_light_pause()
396396
selector = self.__convert_to_css_if_xpath(selector)
397397
if not self.is_element_present(selector):
@@ -414,12 +414,12 @@ def select_all(self, selector, timeout=None):
414414

415415
def find_elements(self, selector, timeout=None):
416416
if not timeout:
417-
timeout = settings.SMALL_TIMEOUT
417+
timeout = settings.MINI_TIMEOUT
418418
return self.select_all(selector, timeout=timeout)
419419

420420
def find_visible_elements(self, selector, timeout=None):
421421
if not timeout:
422-
timeout = settings.SMALL_TIMEOUT
422+
timeout = settings.MINI_TIMEOUT
423423
visible_elements = []
424424
elements = self.select_all(selector, timeout=timeout)
425425
for element in elements:

seleniumbase/undetected/cdp_driver/browser.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,30 @@ def deconstruct_browser():
3939
for _ in __registered__instances__:
4040
if not _.stopped:
4141
_.stop(deconstruct=True)
42-
for attempt in range(5):
42+
max_attempts = 5
43+
for attempt in range(max_attempts):
4344
try:
4445
if _.config and not _.config.uses_custom_data_dir:
45-
shutil.rmtree(_.config.user_data_dir, ignore_errors=False)
46+
if os.path.exists(_.config.user_data_dir):
47+
shutil.rmtree(
48+
_.config.user_data_dir, ignore_errors=False
49+
)
50+
if not os.path.exists(_.config.user_data_dir):
51+
break
52+
else:
53+
time.sleep(0.12)
4654
except FileNotFoundError:
4755
break
4856
except (PermissionError, OSError) as e:
49-
if attempt == 4:
57+
if attempt == max_attempts - 1:
5058
logger.debug(
5159
"Problem removing data dir %s\n"
5260
"Consider checking whether it's there "
5361
"and remove it by hand\nerror: %s"
5462
% (_.config.user_data_dir, e)
5563
)
5664
break
57-
time.sleep(0.15)
65+
time.sleep(0.12)
5866
continue
5967
logging.debug("Temp profile %s was removed." % _.config.user_data_dir)
6068

@@ -207,7 +215,8 @@ async def _handle_target_update(
207215
target_info = event.target_info
208216
current_tab = next(
209217
filter(
210-
lambda item: item.target_id == target_info.target_id, self.targets # noqa
218+
lambda item: item.target_id == target_info.target_id,
219+
self.targets,
211220
)
212221
)
213222
current_target = current_tab.target
@@ -583,11 +592,11 @@ async def start(self=None) -> Browser:
583592
else "c:/path/to/your/browser.exe"
584593
)
585594
)
586-
if getattr(self.config, "_extensions", None): # noqa
595+
if getattr(self.config, "_extensions", None):
587596
self.config.add_argument(
588597
"--load-extension=%s"
589598
% ",".join(str(_) for _ in self.config._extensions)
590-
) # noqa
599+
)
591600
exe = self.config.browser_executable_path
592601
params = self.config()
593602
logger.debug(
@@ -613,16 +622,17 @@ async def start(self=None) -> Browser:
613622
await asyncio.sleep(0.05)
614623
get_registered_instances().add(self)
615624
await asyncio.sleep(0.15)
616-
for attempt in range(5):
625+
max_attempts = 20
626+
for attempt in range(max_attempts):
617627
try:
618628
self.info = ContraDict(
619629
await self._http.get("version"), silent=True
620630
)
621631
except (Exception,):
622-
if attempt == 4:
632+
if attempt == max_attempts - 1:
623633
logger.debug("Could not start", exc_info=True)
624634
else:
625-
await self.sleep(0.5)
635+
await self.sleep(0.2)
626636
else:
627637
break
628638
if not self.info:
@@ -644,9 +654,11 @@ async def start(self=None) -> Browser:
644654
%s
645655
""" % (dashes, message, dashes)
646656
)
657+
await asyncio.sleep(0.03)
647658
self.connection = Connection(
648659
self.info.webSocketDebuggerUrl, browser=self
649660
)
661+
await asyncio.sleep(0.03)
650662
if self.config.autodiscover_targets:
651663
logger.debug("Enabling autodiscover targets")
652664
self.connection.handlers[cdp.target.TargetInfoChanged] = [

seleniumbase/undetected/cdp_driver/cdp_util.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,22 @@ def __activate_virtual_display_as_needed(
115115
"Starting VDisplay from cdp_util: (%s, %s)"
116116
% (_xvfb_width, _xvfb_height)
117117
)
118-
_xvfb_display.start()
118+
try:
119+
_xvfb_display.start()
120+
except Exception:
121+
time.sleep(0.03)
122+
_xvfb_display.start()
123+
time.sleep(0.03)
119124
if "DISPLAY" not in os.environ.keys():
120-
print(
121-
"\n X11 display failed! Is Xvfb installed? "
122-
"\n Try this: `sudo apt install -y xvfb`"
123-
)
124-
__activate_standard_virtual_display()
125+
time.sleep(0.03)
126+
_xvfb_display.start()
127+
time.sleep(0.08)
128+
if "DISPLAY" not in os.environ.keys():
129+
print(
130+
"\n X11 display failed! Is Xvfb installed? "
131+
"\n Try this: `sudo apt install -y xvfb`"
132+
)
133+
__activate_standard_virtual_display()
125134
else:
126135
sb_config._virtual_display = _xvfb_display
127136
sb_config.headless_active = True
@@ -781,15 +790,14 @@ async def create_from_driver(driver) -> Browser:
781790

782791

783792
def free_port() -> int:
784-
"""Determines a free port using sockets."""
793+
"""Find and return a free port number assigned by the OS."""
785794
import socket
786795

787-
free_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
788-
free_socket.bind(("127.0.0.1", 0))
789-
free_socket.listen(5)
790-
port: int = free_socket.getsockname()[1]
791-
free_socket.close()
792-
return port
796+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
797+
# Binding to port 0 lets the OS pick a free port
798+
s.bind(("127.0.0.1", 0))
799+
s.listen(5)
800+
return s.getsockname()[1]
793801

794802

795803
def filter_recurse_all(

setup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@
159159
'filelock~=3.19.1;python_version<"3.10"',
160160
'filelock>=3.25.2;python_version>="3.10"',
161161
'fasteners>=0.20',
162-
'mycdp>=1.3.4',
162+
'mycdp>=1.3.6',
163163
'pynose>=1.5.5',
164164
'platformdirs~=4.4.0;python_version<"3.10"',
165165
'platformdirs>=4.9.4;python_version>="3.10"',
@@ -177,7 +177,7 @@
177177
'tabcompleter>=1.4.0',
178178
'pdbp>=1.8.2',
179179
'idna>=3.11',
180-
'charset-normalizer>=3.4.5,<4',
180+
'charset-normalizer>=3.4.6,<4',
181181
'urllib3>=1.26.20,<2;python_version<"3.10"',
182182
'urllib3>=1.26.20,<3;python_version>="3.10"',
183183
'requests~=2.32.5',
@@ -234,7 +234,7 @@
234234
# Usage: coverage run -m pytest; coverage html; coverage report
235235
"coverage": [
236236
'coverage>=7.10.7;python_version<"3.10"',
237-
'coverage>=7.13.4;python_version>="3.10"',
237+
'coverage>=7.13.5;python_version>="3.10"',
238238
'pytest-cov>=7.0.0',
239239
],
240240
# pip install -e .[flake8]

0 commit comments

Comments
 (0)