Skip to content

Commit b2207cb

Browse files
authored
Merge pull request #4466 from seleniumbase/cdp-mode-patch-129
CDP Mode: Patch 129
2 parents 71128d8 + 0be0f56 commit b2207cb

15 files changed

Lines changed: 341 additions & 318 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

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

14+
<p align="center"><a href="https://trendshift.io/repositories/12493?utm_source=repository-badge&amp;utm_medium=badge&amp;utm_campaign=badge-repository-12493" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/12493" alt="seleniumbase%2FSeleniumBase | Trendshift" width="250" height="55"/></a></p>
15+
1416
<p align="center"><a href="https://pypi.python.org/pypi/seleniumbase" target="_blank"><img src="https://img.shields.io/pypi/v/seleniumbase.svg?color=3399EE" alt="PyPI version" /></a> <a href="https://pepy.tech/projects/seleniumbase?timeRange=threeMonths&category=version&includeCIDownloads=true&granularity=daily&viewType=line&versions=*" target="_blank"><img src="https://static.pepy.tech/badge/seleniumbase" alt="SeleniumBase PyPI downloads" /></a> <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-22BBCC.svg" title="SeleniumBase" /></a> <a href="https://seleniumbase.io"><img src="https://img.shields.io/badge/docs-seleniumbase.io-11BBAA.svg" alt="SeleniumBase Docs" /></a> <a href="https://github.com/seleniumbase/SeleniumBase/actions" target="_blank"><img src="https://github.com/seleniumbase/SeleniumBase/workflows/Tests/badge.svg" alt="SeleniumBase GitHub Actions" /></a> <a href="https://www.youtube.com/playlist?list=PLp9uKicxkBc5UIlGi2BuE3aWC7JyXpD3m"><img src="https://img.shields.io/badge/docs-📺-F12345.svg?style=flat&logo=YouTube&logoColor=white&label=YouTube" alt="YouTube Channel" /></a></p>
1517

1618
<p align="center">
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import json
2+
from seleniumbase import sb_cdp
3+
4+
sb = sb_cdp.Chrome()
5+
sb.goto("https://quotes.toscrape.com/")
6+
all_quotes = []
7+
while True:
8+
quotes = sb.find_elements("div.quote")
9+
for quote in quotes:
10+
all_quotes.append(
11+
{
12+
"author": quote.query_selector("span small").text,
13+
"text": quote.query_selector("span.text").text,
14+
}
15+
)
16+
if sb.is_element_visible("li.next"):
17+
sb.click("li.next")
18+
else:
19+
break
20+
file_path = "downloaded_files/quotes.json"
21+
with open(file_path, "w", encoding="utf-8") as outfile:
22+
json.dump(all_quotes, outfile)
23+
print(f'Quotes saved to "{file_path}".')
24+
sb.quit()

examples/cdp_mode/raw_cdp_yelp.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import json
2+
from seleniumbase import sb_cdp
3+
4+
sb = sb_cdp.Chrome()
5+
sb.goto("https://www.yelp.com/")
6+
sb.sleep(3)
7+
restaurants_menu = 'button[aria-label*="Restaurants"]'
8+
cafes = 'span:contains("Cafes")'
9+
sb.wait_for_element(restaurants_menu)
10+
sb.sleep(1)
11+
sb.hover_and_click(restaurants_menu, cafes)
12+
sb.sleep(3)
13+
all_cafes = []
14+
counter = 0
15+
max_pages = 3
16+
while True:
17+
dtci = '[data-traffic-crawl-id="SearchResult%s"]'
18+
cafes = sb.find_elements('[data-testid="serp-ia-card"]')
19+
for cafe in cafes:
20+
name = cafe.query_selector(dtci % "BizName")
21+
rating = cafe.query_selector(dtci % "BizRating")
22+
snippet = cafe.query_selector(dtci % "ReviewSnippet")
23+
if name and rating and snippet:
24+
all_cafes.append(
25+
{
26+
"name": name.text,
27+
"rating": rating.text,
28+
"snippet": snippet.text,
29+
}
30+
)
31+
counter += 1
32+
if counter >= max_pages:
33+
break
34+
if sb.is_element_visible('span:contains("Next Page")'):
35+
sb.click('span:contains("Next Page")')
36+
sb.sleep(1)
37+
else:
38+
break
39+
file_path = "downloaded_files/cafes.json"
40+
with open(file_path, "w", encoding="utf-8") as outfile:
41+
json.dump(all_cafes, outfile)
42+
print(f'Cafes saved to "{file_path}".')
43+
sb.quit()

mkdocs_build/requirements.txt

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

44
regex>=2026.7.19
55
pymdown-extensions>=10.21.3
6-
pipdeptree>=4.2.0
6+
pipdeptree>=4.2.1
77
python-dateutil>=2.8.2
88
click>=8.4.2
99
Markdown==3.10.3
@@ -15,7 +15,7 @@ Babel==2.18.0
1515
paginate==0.5.7
1616
mkdocs==1.6.1
1717
mkdocs-material==9.6.23
18-
mkdocs-exclude-search==0.6.6
18+
mkdocs-exclude-search==0.6.7
1919
mkdocs-simple-hooks==0.1.5
2020
mkdocs-get-deps==0.2.2
2121
mkdocs-material-extensions==1.3.1

requirements.txt

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
1-
pip>=26.0.1;python_version<"3.10"
2-
pip>=26.2.1;python_version>="3.10"
1+
pip>=26.2.1
32
packaging>=26.3
4-
setuptools~=70.2;python_version<"3.10"
5-
setuptools>=84.0.0;python_version>="3.10"
6-
wheel>=0.47.0
3+
setuptools>=84.0.0
4+
wheel>=0.48.0
75
attrs>=26.1.0
86
certifi>=2026.7.22
97
exceptiongroup>=1.3.1
10-
websockets~=15.0.1;python_version<"3.10"
118
websockets~=16.1.1;python_version=="3.10"
12-
websockets>=17.0.1;python_version>="3.11"
13-
filelock~=3.19.1;python_version<"3.10"
14-
filelock>=3.32.2;python_version>="3.10"
9+
websockets>=16.1.1;python_version>="3.11"
10+
filelock>=3.32.3
1511
fasteners>=0.20
1612
mycdp>=1.4.0
1713
pynose>=1.5.5
18-
platformdirs~=4.4.0;python_version<"3.10"
19-
platformdirs>=4.11.2;python_version>="3.10"
14+
platformdirs>=4.11.3
2015
typing-extensions>=4.16.0
2116
sbvirtualdisplay>=1.4.0
2217
MarkupSafe>=3.0.3
@@ -27,62 +22,50 @@ parse-type>=0.6.6
2722
colorama>=0.4.6
2823
psutil>=7.2.2
2924
pyyaml>=6.0.3
30-
pygments>=2.20.0
25+
pygments>=2.20.1
3126
pyreadline3>=3.5.4;platform_system=="Windows"
3227
tabcompleter>=1.4.1
3328
pdbp>=1.8.2
34-
idna>=3.18
35-
charset-normalizer>=3.4.9,<4
36-
urllib3>=1.26.20,<2;python_version<"3.10"
37-
urllib3>=2.7.0,<3;python_version>="3.10"
38-
requests~=2.32.5;python_version<"3.10"
39-
requests~=2.34.2;python_version>="3.10"
29+
idna>=3.19
30+
charset-normalizer>=3.5.1,<4
31+
urllib3>=2.7.0,<3
32+
requests~=2.34.2
4033
sniffio==1.3.1
4134
h11==0.16.0
4235
outcome==1.3.0.post0
43-
trio>=0.31.0,<1;python_version<"3.10"
44-
trio>=0.33.0,<1;python_version>="3.10"
36+
trio>=0.34.0,<1
4537
trio-websocket~=0.12.2
46-
wsproto==1.2.0;python_version<"3.10"
47-
wsproto~=1.3.2;python_version>="3.10"
38+
wsproto~=1.3.2
4839
websocket-client~=1.9.0
49-
selenium==4.32.0;python_version<"3.10"
50-
selenium==4.46.0;python_version>="3.10"
51-
cssselect==1.3.0;python_version<"3.10"
52-
cssselect>=1.5.0,<2;python_version>="3.10"
40+
selenium==4.47.0
41+
cssselect>=1.5.0,<2
5342
sortedcontainers==2.4.0
54-
execnet==2.1.1;python_version<"3.10"
55-
execnet==2.1.2;python_version>="3.10"
56-
iniconfig==2.1.0;python_version<"3.10"
57-
iniconfig==2.3.0;python_version>="3.10"
43+
execnet==2.1.2
44+
iniconfig==2.3.0
5845
pluggy==1.6.0
5946
pytest==8.4.2;python_version<"3.11"
6047
pytest==9.1.1;python_version>="3.11"
6148
pytest-html==4.0.2
6249
pytest-metadata==3.1.1
6350
pytest-ordering==0.6
64-
pytest-rerunfailures==16.0.1;python_version<"3.10"
6551
pytest-rerunfailures==16.3;python_version=="3.10"
66-
pytest-rerunfailures==16.4;python_version>="3.11"
52+
pytest-rerunfailures==16.6;python_version>="3.11"
6753
pytest-xdist==3.8.0
6854
parameterized==0.9.0
6955
behave==1.2.6
70-
soupsieve~=2.8.4;python_version<"3.10"
71-
soupsieve~=2.9.2;python_version>="3.10"
56+
soupsieve~=2.9.2
7257
beautifulsoup4~=4.15.0
7358
pyotp~=2.10.0
7459
python-xlib==0.33;platform_system=="Linux"
7560
PyAutoGUI>=0.9.54;platform_system=="Linux"
76-
markdown-it-py==3.0.0;python_version<"3.10"
77-
markdown-it-py==4.2.0;python_version>="3.10"
61+
markdown-it-py==4.2.0
7862
mdurl==0.1.2
7963
rich>=15.0.0,<16
8064

8165
# --- Testing Requirements --- #
8266
# ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.)
8367

84-
coverage>=7.10.7;python_version<"3.10"
85-
coverage>=7.15.4;python_version>="3.10"
68+
coverage>=7.15.4
8669
pytest-cov>=7.1.0
8770
flake8==7.3.0
8871
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.51.12"
2+
__version__ = "4.52.0"

seleniumbase/console_scripts/sb_install.py

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -659,8 +659,8 @@ def main(override=None, intel_for_uc=None, force_uc=None):
659659
else:
660660
not_latest = c5 + "(" + c4 + "Legacy Version" + c5 + ")" + cr
661661
p_version = p_version + " " + not_latest
662-
msg = c2 + "chromedriver to download" + cr
663-
log_d("\n*** %s = %s" % (msg, p_version))
662+
msg = c2 + "chromedriver" + cr
663+
log_d("\n*** Getting %s %s" % (msg, p_version))
664664
else:
665665
raise Exception("Could not find chromedriver to download!\n")
666666
if not get_latest:
@@ -704,9 +704,9 @@ def main(override=None, intel_for_uc=None, force_uc=None):
704704
use_version = found_version
705705
if not use_version:
706706
use_version = get_cft_latest_version_from_milestone(major_version)
707-
msg = c2 + "Chrome for Testing to download" + cr
707+
msg = c2 + "Chrome for Testing" + cr
708708
p_version = c3 + use_version + cr
709-
log_d("\n*** %s = %s" % (msg, p_version))
709+
log_d("\n*** Getting %s %s" % (msg, p_version))
710710
if IS_MAC:
711711
if IS_ARM_MAC:
712712
platform_code = "mac-arm64"
@@ -813,9 +813,9 @@ def main(override=None, intel_for_uc=None, force_uc=None):
813813
use_version = found_version
814814
if not use_version:
815815
use_version = get_cft_latest_version_from_milestone(major_version)
816-
msg = c2 + "Chrome-Headless-Shell to download" + cr
816+
msg = c2 + "Chrome-Headless-Shell" + cr
817817
p_version = c3 + use_version + cr
818-
log_d("\n*** %s = %s" % (msg, p_version))
818+
log_d("\n*** Getting %s %s" % (msg, p_version))
819819
if IS_MAC:
820820
if IS_ARM_MAC:
821821
platform_code = "mac-arm64"
@@ -892,9 +892,9 @@ def main(override=None, intel_for_uc=None, force_uc=None):
892892
if not found_geckodriver:
893893
url_request = requests_get(download_url)
894894
if found_geckodriver or url_request.ok:
895-
msg = c2 + "geckodriver to download" + cr
895+
msg = c2 + "geckodriver" + cr
896896
p_version = c3 + use_version + cr
897-
log_d("\n*** %s = %s" % (msg, p_version))
897+
log_d("\n*** Getting %s %s" % (msg, p_version))
898898
else:
899899
raise Exception(
900900
"\nCould not find the specified geckodriver "
@@ -999,9 +999,9 @@ def main(override=None, intel_for_uc=None, force_uc=None):
999999
raise Exception(
10001000
"Could not find version [%s] of EdgeDriver!" % use_version
10011001
)
1002-
msg = c2 + "edgedriver to download" + cr
1002+
msg = c2 + "edgedriver" + cr
10031003
p_version = c3 + use_version + cr
1004-
log_d("\n*** %s = %s" % (msg, p_version))
1004+
log_d("\n*** Getting %s %s" % (msg, p_version))
10051005
elif name == "iedriver":
10061006
full_version = "4.14.0"
10071007
use_version = full_version
@@ -1029,9 +1029,9 @@ def main(override=None, intel_for_uc=None, force_uc=None):
10291029
url_request = requests_get_with_retry(headless_ie_url)
10301030
if url_request.ok:
10311031
headless_ie_exists = True
1032-
msg = c2 + "HeadlessIEDriver to download" + cr
1032+
msg = c2 + "HeadlessIEDriver" + cr
10331033
p_version = c3 + headless_ie_version + cr
1034-
log_d("\n*** %s = %s" % (msg, p_version))
1034+
log_d("\n*** Getting %s %s" % (msg, p_version))
10351035
else:
10361036
invalid_run_command()
10371037

@@ -1095,7 +1095,7 @@ def main(override=None, intel_for_uc=None, force_uc=None):
10951095
os.remove(new_file)
10961096
if not driver_file or not driver_path or not filename:
10971097
raise Exception("headless_ie_selenium.exe missing from Zip file!")
1098-
log_d("Extracting %s from %s ..." % (filename, headless_ie_file_name))
1098+
log_d("Extracting %s from %s:" % (filename, headless_ie_file_name))
10991099
zip_ref.extractall(downloads_folder)
11001100
zip_ref.close()
11011101
os.remove(zip_file_path)
@@ -1120,13 +1120,12 @@ def main(override=None, intel_for_uc=None, force_uc=None):
11201120
os.rmdir(os.path.join(downloads_folder, h_ie_fn))
11211121
driver_path = os.path.join(downloads_folder, filename)
11221122
log_d(
1123-
"The file [%s] was saved to:\n%s%s%s\n"
1123+
"['%s'] was saved to:\n%s%s%s\n"
11241124
% (filename, c3, driver_path, cr)
11251125
)
1126-
log_d("Making [%s %s] executable ..." % (driver_file, use_version))
11271126
make_executable(driver_path)
11281127
log_d(
1129-
"%s[%s %s] is now ready for use!%s"
1128+
"%s[%s %s] is ready for use!%s"
11301129
% (c1, driver_file, use_version, cr)
11311130
)
11321131

@@ -1180,7 +1179,7 @@ def main(override=None, intel_for_uc=None, force_uc=None):
11801179
os.remove(new_file) # Technically the old file now
11811180
if driver_contents:
11821181
contents = driver_contents
1183-
log_d("Extracting %s from %s ..." % (contents, file_name))
1182+
log_d("Extracting %s from %s:" % (contents, file_name))
11841183
if name == "uc_driver":
11851184
f_name = "uc_driver"
11861185
new_file = os.path.join(downloads_folder, f_name)
@@ -1232,11 +1231,10 @@ def main(override=None, intel_for_uc=None, force_uc=None):
12321231
d_folder = os.sep.join(pr_file.split(os.sep)[:-1]) + os.sep
12331232
d_file = pr_file.split(os.sep)[-1]
12341233
d_ff = c3 + d_folder + cr + "\n" + c3 + d_file + cr
1235-
log_d("The file [%s] was saved to:\n%s\n" % (f_name, d_ff))
1236-
log_d("Making [%s %s] executable ..." % (f_name, use_version))
1234+
log_d("['%s'] was saved to:\n%s\n" % (f_name, d_ff))
12371235
make_executable(new_file)
12381236
log_d(
1239-
"%s[%s %s] is now ready for use!%s" %
1237+
"%s[%s %s] is ready for use!%s" %
12401238
(c1, f_name, use_version, cr)
12411239
)
12421240
if copy_to_path and os.path.exists(LOCAL_PATH):
@@ -1301,7 +1299,7 @@ def main(override=None, intel_for_uc=None, force_uc=None):
13011299
if str_name == "IEDriverServer.exe":
13021300
raise Exception("IEDriverServer missing from Zip file!")
13031301
raise Exception("msedgedriver missing from Zip file!")
1304-
log_d("Extracting %s from %s ..." % (contents, file_name))
1302+
log_d("Extracting %s from %s:" % (contents, file_name))
13051303
zip_ref.extractall(downloads_folder)
13061304
zip_ref.close()
13071305
os.remove(zip_file_path)
@@ -1323,13 +1321,12 @@ def main(override=None, intel_for_uc=None, force_uc=None):
13231321
pr_sep = c3 + os.sep + cr
13241322
pr_driver_file = c3 + driver_file + cr
13251323
log_d(
1326-
"The file [%s] was saved to:\n%s%s\n%s\n"
1324+
"['%s'] was saved to:\n%s%s\n%s\n"
13271325
% (driver_file, pr_driver_base, pr_sep, pr_driver_file)
13281326
)
1329-
log_d("Making [%s %s] executable ..." % (driver_file, use_version))
13301327
make_executable(driver_path)
13311328
log_d(
1332-
"%s[%s %s] is now ready for use!%s"
1329+
"%s[%s %s] is ready for use!%s"
13331330
% (c1, driver_file, use_version, cr)
13341331
)
13351332
if copy_to_path and os.path.exists(LOCAL_PATH):
@@ -1462,7 +1459,7 @@ def main(override=None, intel_for_uc=None, force_uc=None):
14621459
if "Driver" in new_file or "driver" in new_file:
14631460
if os.path.exists(new_file):
14641461
os.remove(new_file) # Technically the old file now
1465-
log_d("Extracting %s from %s ..." % (contents, file_name))
1462+
log_d("Extracting %s from %s:" % (contents, file_name))
14661463
if sys.version_info < (3, 12):
14671464
tar.extractall(downloads_folder)
14681465
else:
@@ -1473,11 +1470,10 @@ def main(override=None, intel_for_uc=None, force_uc=None):
14731470
for f_name in contents:
14741471
new_file = os.path.join(downloads_folder, str(f_name))
14751472
pr_file = c3 + new_file + cr
1476-
log_d("The file [%s] was saved to:\n%s\n" % (f_name, pr_file))
1477-
log_d("Making [%s %s] executable ..." % (f_name, use_version))
1473+
log_d("['%s'] was saved to:\n%s\n" % (f_name, pr_file))
14781474
make_executable(new_file)
14791475
log_d(
1480-
"%s[%s %s] is now ready for use!%s"
1476+
"%s[%s %s] is ready for use!%s"
14811477
% (c1, f_name, use_version, cr)
14821478
)
14831479
if copy_to_path and os.path.exists(LOCAL_PATH):
@@ -1494,9 +1490,8 @@ def main(override=None, intel_for_uc=None, force_uc=None):
14941490
# Not a .zip file or a .tar.gz file. Just a direct download.
14951491
if "Driver" in file_name or "driver" in file_name:
14961492
log_d("%sDownload Complete!%s\n" % (c1, cr))
1497-
log_d("Making [%s] executable ..." % file_name)
14981493
make_executable(file_path)
1499-
log_d("%s[%s] is now ready for use!%s" % (c1, file_name, cr))
1494+
log_d("%s[%s] is ready for use!%s" % (c1, file_name, cr))
15001495
log_d("Location of [%s]:\n%s\n" % (file_name, file_path))
15011496

15021497

0 commit comments

Comments
 (0)