Skip to content

Commit 1ab98f5

Browse files
authored
Merge pull request #4252 from seleniumbase/cdp-and-docker-updates
CDP and Docker updates
2 parents 9289014 + 3a18c8a commit 1ab98f5

File tree

11 files changed

+129
-39
lines changed

11 files changed

+129
-39
lines changed

Dockerfile

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# SeleniumBase Docker Image
2-
FROM ubuntu:22.04
2+
FROM ubuntu:24.04
33
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
44
ENV PYTHONUNBUFFERED=1
55
ENV PYTHONIOENCODING=UTF-8
6+
ENV DEBIAN_FRONTEND=noninteractive
67

78
#======================
89
# Locale Configuration
@@ -23,49 +24,39 @@ RUN locale-gen en_US.UTF-8
2324
#===========================
2425
# Fingerprint Configuration
2526
#===========================
26-
RUN apt install -y xvfb fonts-liberation fonts-noto-color-emoji libvulkan1 libnss3 libatk-bridge2.0-0 libcups2 libxcomposite1 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2
27+
RUN apt-get update
28+
RUN apt install -y fonts-liberation fonts-noto-color-emoji libvulkan1 libnss3 libatk-bridge2.0-0 libcups2 libxcomposite1 libxrandr2 libgbm1 libpango-1.0-0 libcairo2
2729
RUN apt install -y fonts-freefont-ttf fonts-dejavu-core fonts-ubuntu fonts-roboto fonts-droid-fallback
2830

2931
#======================
3032
# Install Common Fonts
3133
#======================
3234
RUN apt-get update
3335
RUN apt-get install -y \
34-
fonts-liberation \
3536
fonts-liberation2 \
3637
fonts-font-awesome \
37-
fonts-ubuntu \
3838
fonts-terminus \
3939
fonts-powerline \
4040
fonts-open-sans \
4141
fonts-mononoki \
42-
fonts-roboto \
4342
fonts-lato
4443

4544
#============================
4645
# Install Linux Dependencies
4746
#============================
4847
RUN apt-get update
4948
RUN apt-get install -y \
50-
libasound2 \
51-
libatk-bridge2.0-0 \
5249
libatk1.0-0 \
5350
libatspi2.0-0 \
54-
libcups2 \
5551
libdbus-1-3 \
5652
libdrm2 \
57-
libgbm1 \
5853
libgtk-3-0 \
5954
libnspr4 \
60-
libnss3 \
6155
libu2f-udev \
62-
libvulkan1 \
6356
libwayland-client0 \
64-
libxcomposite1 \
6557
libxdamage1 \
6658
libxfixes3 \
67-
libxkbcommon0 \
68-
libxrandr2
59+
libxkbcommon0
6960

7061
#==========================
7162
# Install useful utilities
@@ -96,13 +87,20 @@ RUN rm ./google-chrome-stable_current_amd64.deb
9687
#================
9788
# Install Python
9889
#================
90+
RUN apt-get update && apt-get install -y software-properties-common
91+
RUN add-apt-repository ppa:deadsnakes/ppa -y
92+
RUN apt-get update
93+
RUN apt-get install -y python3.13 python3.13-venv python3.13-dev build-essential
94+
RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.13 1
95+
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
96+
RUN python3.13 -m ensurepip --upgrade
97+
RUN python3.13 -m pip install --upgrade pip
9998
RUN apt-get update
100-
RUN apt-get install -y python3 python3-pip python3-setuptools python3-dev python3-tk
99+
RUN apt-get install -y python3.13-tk python3.13-dev
101100
RUN alias python=python3
102101
RUN echo "alias python=python3" >> ~/.bashrc
103-
RUN apt-get -qy --no-install-recommends install python3.10
104102
RUN rm /usr/bin/python3
105-
RUN ln -s python3.10 /usr/bin/python3
103+
RUN ln -s python3.13 /usr/bin/python3
106104

107105
#===============
108106
# Cleanup Lists
@@ -128,6 +126,8 @@ RUN pip install --upgrade pip setuptools wheel
128126
RUN cd /SeleniumBase && ls && pip install -r requirements.txt --upgrade
129127
RUN cd /SeleniumBase && pip install .
130128
RUN pip install pyautogui
129+
RUN pip install playwright
130+
RUN seleniumbase get chromium
131131

132132
#=======================
133133
# Download chromedriver

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,16 @@ pytest test_demo_site.py
186186

187187
--------
188188

189+
<p align="left">📓 Here's a high-level stealthy architecture overview of SeleniumBase:</p>
190+
191+
192+
193+
<img src="https://seleniumbase.github.io/other/sb_stealth.png" width="650" alt="High-Level Stealthy Architecture Overview" title="High-Level Stealthy Architecture Overview" />
194+
195+
(For maximum stealth, use <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md">CDP Mode</a>, which is used by <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/playwright/ReadMe.md">Stealthy Playwright Mode</a>)
196+
197+
--------
198+
189199
<p align="left"><a href="https://github.com/seleniumbase/SeleniumBase/"><img src="https://seleniumbase.github.io/cdn/img/super_logo_sb3.png" alt="SeleniumBase" title="SeleniumBase" width="232" /></a></p>
190200

191201
<blockquote>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""(Bypasses the DataDome slider CAPTCHA)"""
2+
from playwright.sync_api import sync_playwright
3+
from seleniumbase import SB
4+
5+
with SB(uc=True, locale="es") as sb:
6+
url = "https://www.idealista.com/venta-viviendas/barcelona-provincia/"
7+
sb.activate_cdp_mode(url)
8+
sb.sleep(1)
9+
sb.solve_captcha()
10+
sb.sleep(2)
11+
endpoint_url = sb.cdp.get_endpoint_url()
12+
13+
with sync_playwright() as p:
14+
browser = p.chromium.connect_over_cdp(endpoint_url)
15+
context = browser.contexts[0]
16+
page = context.pages[0]
17+
page.click("button#didomi-notice-agree-button")
18+
page.wait_for_timeout(1000)
19+
print("*** " + page.locator("h1").inner_text())
20+
items = page.locator("div.item-info-container")
21+
for i in range(items.count()):
22+
item = items.nth(i)
23+
print(item.locator("a.item-link").text_content().strip())
24+
print(item.locator("span.item-price").text_content().strip())
25+
item_text = items.nth(i)

examples/cdp_mode/raw_idealista.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""(Bypasses the DataDome slider CAPTCHA)"""
2+
from seleniumbase import SB
3+
4+
with SB(uc=True, test=True, locale="es") as sb:
5+
url = "https://www.idealista.com/venta-viviendas/barcelona-provincia/"
6+
sb.activate_cdp_mode(url)
7+
sb.sleep(1)
8+
sb.solve_captcha()
9+
sb.sleep(2)
10+
sb.click("button#didomi-notice-agree-button")
11+
print("*** " + sb.get_text("h1"))
12+
items = sb.find_elements("div.item-info-container")
13+
for item in items:
14+
print(item.querySelector("a.item-link").text)
15+
print(item.querySelector("span.item-price").text)

mkdocs_build/requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# mkdocs dependencies for generating the seleniumbase.io website
22
# Minimum Python version: 3.10 (for generating docs only)
33

4-
regex>=2026.1.15
5-
pymdown-extensions>=10.20.1
6-
pipdeptree>=2.30.0
4+
regex>=2026.2.19
5+
pymdown-extensions>=10.21
6+
pipdeptree>=2.31.0
77
python-dateutil>=2.8.2
88
Markdown==3.10.2
99
click==8.3.1

requirements.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@ exceptiongroup>=1.3.1
99
websockets~=15.0.1;python_version<"3.10"
1010
websockets>=16.0;python_version>="3.10"
1111
filelock~=3.19.1;python_version<"3.10"
12-
filelock>=3.20.3;python_version>="3.10"
12+
filelock>=3.24.3;python_version>="3.10"
1313
fasteners>=0.20
1414
mycdp>=1.3.2
1515
pynose>=1.5.5
1616
platformdirs~=4.4.0;python_version<"3.10"
17-
platformdirs>=4.5.1;python_version>="3.10"
17+
platformdirs>=4.9.2;python_version>="3.10"
1818
typing-extensions>=4.15.0
1919
sbvirtualdisplay>=1.4.0
2020
MarkupSafe>=3.0.3
2121
Jinja2>=3.1.6
2222
six>=1.17.0
23-
parse>=1.21.0
23+
parse>=1.21.1
2424
parse-type>=0.6.6
2525
colorama>=0.4.6
2626
pyyaml>=6.0.3
@@ -38,13 +38,13 @@ sniffio==1.3.1
3838
h11==0.16.0
3939
outcome==1.3.0.post0
4040
trio>=0.31.0,<1;python_version<"3.10"
41-
trio>=0.32.0,<1;python_version>="3.10"
41+
trio>=0.33.0,<1;python_version>="3.10"
4242
trio-websocket~=0.12.2
4343
wsproto==1.2.0;python_version<"3.10"
4444
wsproto~=1.3.2;python_version>="3.10"
4545
websocket-client~=1.9.0
4646
selenium==4.32.0;python_version<"3.10"
47-
selenium==4.40.0;python_version>="3.10"
47+
selenium==4.41.0;python_version>="3.10"
4848
cssselect==1.3.0;python_version<"3.10"
4949
cssselect>=1.4.0,<2;python_version>="3.10"
5050
nest-asyncio==1.6.0
@@ -72,7 +72,7 @@ PyAutoGUI>=0.9.54;platform_system=="Linux"
7272
markdown-it-py==3.0.0;python_version<"3.10"
7373
markdown-it-py==4.0.0;python_version>="3.10"
7474
mdurl==0.1.2
75-
rich>=14.3.2,<15
75+
rich>=14.3.3,<15
7676

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

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.46.5"
2+
__version__ = "4.47.0"

seleniumbase/core/browser_launcher.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,9 +1501,12 @@ def _uc_gui_click_captcha(
15011501
):
15021502
frame = '[data-callback="onCaptchaSuccess"]'
15031503
elif driver.is_element_present(
1504-
"div:not([class]) > div:not([class])"
1504+
"div:not([class]):not([id]) > div:not([class]):not([id])"
15051505
):
1506-
frame = "div:not([class]) > div:not([class])"
1506+
frame = (
1507+
"div:not([class]):not([id]) > "
1508+
"div:not([class]):not([id])"
1509+
)
15071510
else:
15081511
return False
15091512
if (

seleniumbase/core/sb_cdp.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1981,6 +1981,16 @@ def _on_an_incapsula_hcaptcha_page(self, *args, **kwargs):
19811981
return True
19821982
return False
19831983

1984+
def _on_a_datadome_slider_page(self, *args, **kwargs):
1985+
self.loop.run_until_complete(self.page.wait())
1986+
if (
1987+
self.is_element_visible(
1988+
'body > iframe[src*="/geo.captcha-delivery.com/captcha/"]'
1989+
)
1990+
):
1991+
return True
1992+
return False
1993+
19841994
def _on_a_g_recaptcha_page(self, *args, **kwargs):
19851995
time.sleep(0.4) # reCAPTCHA may need a moment to appear
19861996
self.loop.run_until_complete(self.page.wait())
@@ -2054,6 +2064,26 @@ def __gui_click_recaptcha(self, use_cdp=False):
20542064
return True
20552065
return False
20562066

2067+
def __gui_slide_datadome_captcha(self):
2068+
iframe = 'body > iframe[src*="/geo.captcha-delivery.com/captcha/"]'
2069+
if not self.is_element_visible(iframe):
2070+
return False
2071+
src = self.get_attribute(iframe, "src")
2072+
tab = self.get_active_tab()
2073+
self.open_new_tab(url=src)
2074+
time.sleep(0.41)
2075+
self.loop.run_until_complete(self.page.wait())
2076+
time.sleep(0.25)
2077+
x1, y1 = self.get_gui_element_center("div.slider")
2078+
x2, y2 = self.get_gui_element_center("div.sliderTarget")
2079+
self.close_active_tab()
2080+
self.switch_to_tab(tab)
2081+
self.gui_drag_drop_points(x1, y1, x2, y2, timeframe=0.55)
2082+
time.sleep(0.25)
2083+
self.loop.run_until_complete(self.page.wait())
2084+
time.sleep(0.15)
2085+
return True
2086+
20572087
def __cdp_click_incapsula_hcaptcha(self):
20582088
selector = "iframe[data-hcaptcha-widget-id]"
20592089
if self.is_element_visible('iframe[src*="_Incapsula_Resource?"]'):
@@ -2128,6 +2158,9 @@ def __click_captcha(self, use_cdp=False):
21282158
elif self._on_an_incapsula_hcaptcha_page():
21292159
result = self.__cdp_click_incapsula_hcaptcha()
21302160
return result
2161+
elif self._on_a_datadome_slider_page():
2162+
result = self.__gui_slide_datadome_captcha()
2163+
return result
21312164
else:
21322165
return False
21332166
selector = None
@@ -2174,9 +2207,11 @@ def __click_captcha(self, use_cdp=False):
21742207
):
21752208
selector = '[data-callback="onCaptchaSuccess"]'
21762209
elif self.is_element_present(
2177-
"div:not([class]) > div:not([class])"
2210+
"div:not([class]):not([id]) > div:not([class]):not([id])"
21782211
):
2179-
selector = "div:not([class]) > div:not([class])"
2212+
selector = (
2213+
"div:not([class]):not([id]) > div:not([class]):not([id])"
2214+
)
21802215
else:
21812216
return False
21822217
if not selector:

seleniumbase/undetected/cdp_driver/tab.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,9 +1669,11 @@ async def solve_captcha(self):
16691669
):
16701670
selector = '[data-callback="onCaptchaSuccess"]'
16711671
elif await self.is_element_present(
1672-
"div:not([class]) > div:not([class])"
1672+
"div:not([class]):not([id]) > div:not([class]):not([id])"
16731673
):
1674-
selector = "div:not([class]) > div:not([class])"
1674+
selector = (
1675+
"div:not([class]):not([id]) > div:not([class]):not([id])"
1676+
)
16751677
else:
16761678
return False
16771679
if not selector:

0 commit comments

Comments
 (0)