Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions examples/cdp_mode/playwright/raw_seatgeek_sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome(locale="en", ad_block=True)
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(endpoint_url)
context = browser.contexts[0]
page = context.pages[0]
page.goto("https://seatgeek.com/")
input_field = 'input[name="search"]'
page.wait_for_selector(input_field)
sb.sleep(1.6)
query = "Jerry Seinfeld"
sb.press_keys(input_field, query)
sb.sleep(1.6)
page.click("li#active-result-item")
sb.sleep(4.2)
print('*** SeatGeek Search for "%s":' % query)
items = page.locator('[data-testid="listing-item"]')
for i in range(items.count()):
item_text = items.nth(i).inner_text()
print(item_text.replace("\n\n", "\n"))
2 changes: 1 addition & 1 deletion examples/cdp_mode/raw_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


async def main():
url = "seleniumbase.io/simple/login"
url = "https://seleniumbase.io/simple/login"
driver = await cdp_driver.start_async()
page = await driver.get(url, lang="en")
print(await page.get_title())
Expand Down
3 changes: 1 addition & 2 deletions examples/cdp_mode/raw_basic_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


async def main():
url = "seleniumbase.io/simple/login"
url = "https://seleniumbase.io/simple/login"
driver = await cdp_driver.start_async()
page = await driver.get(url, lang="en")
print(await page.get_title())
Expand All @@ -21,7 +21,6 @@ async def main():
driver.stop()

if __name__ == "__main__":
# Call an async function with awaited methods
loop = asyncio.new_event_loop()
with decorators.print_runtime("raw_basic_async.py"):
loop.run_until_complete(main())
17 changes: 17 additions & 0 deletions examples/cdp_mode/raw_basic_cdp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from seleniumbase import sb_cdp

url = "https://seleniumbase.io/simple/login"
sb = sb_cdp.Chrome(url)
sb.type("#username", "demo_user")
sb.type("#password", "secret_pass")
sb.click('a:contains("Sign in")')
sb.assert_exact_text("Welcome!", "h1")
sb.assert_element("img#image1")
sb.highlight("#image1")
top_nav = sb.find_element("div.topnav")
links = top_nav.query_selector_all("a")
for nav_item in links:
print(nav_item.text)
sb.click_link("Sign out")
sb.assert_text("signed out", "#top_message")
sb.driver.stop()
25 changes: 25 additions & 0 deletions examples/cdp_mode/raw_cdp_login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from seleniumbase import decorators
from seleniumbase import sb_cdp


def main():
url = "https://seleniumbase.io/simple/login"
sb = sb_cdp.Chrome(url)
sb.type("#username", "demo_user")
sb.type("#password", "secret_pass")
sb.click('a:contains("Sign in")')
sb.assert_exact_text("Welcome!", "h1")
sb.assert_element("img#image1")
sb.highlight("#image1")
top_nav = sb.find_element("div.topnav")
links = top_nav.query_selector_all("a")
for nav_item in links:
print(nav_item.text)
sb.click_link("Sign out")
sb.assert_text("signed out", "#top_message")
sb.driver.stop()


if __name__ == "__main__":
with decorators.print_runtime("raw_cdp_login.py"):
main()
6 changes: 3 additions & 3 deletions examples/cdp_mode/raw_cf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
with SB(uc=True, test=True, locale="en", guest=True) as sb:
url = "https://www.cloudflare.com/login"
sb.activate_cdp_mode(url)
sb.sleep(4)
sb.sleep(3)
sb.uc_gui_handle_captcha() # PyAutoGUI press Tab and Spacebar
sb.sleep(2)
sb.sleep(3)

with SB(uc=True, test=True, locale="en", guest=True) as sb:
url = "https://www.cloudflare.com/login"
sb.activate_cdp_mode(url)
sb.sleep(4)
sb.uc_gui_click_captcha() # PyAutoGUI click. (Linux needs it)
sb.sleep(2)
sb.sleep(3)
8 changes: 8 additions & 0 deletions examples/cdp_mode/raw_cf_captcha.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from seleniumbase import SB

with SB(uc=True, test=True, guest=True) as sb:
url = "https://www.cloudflare.com/login"
sb.activate_cdp_mode(url)
sb.sleep(3)
sb.solve_captcha()
sb.sleep(3)
34 changes: 34 additions & 0 deletions examples/cdp_mode/raw_homedepot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
url = "https://www.homedepot.com/"
sb.activate_cdp_mode(url)
sb.sleep(1.8)
search_box = "input#typeahead-search-field-input"
search = "Computer Chair"
category = "Gaming Chairs"
required_text = "Chair"
sb.click(search_box)
sb.sleep(1.2)
sb.press_keys(search_box, search)
sb.sleep(0.6)
sb.click("button#typeahead-search-icon-button")
sb.sleep(3.8)
sb.click('a[aria-label="%s"]' % category)
sb.sleep(3.2)
print('*** Home Depot Search for "%s":' % search)
print(' (Results must contain "%s".)' % required_text)
unique_item_text = []
items = sb.find_elements('div[data-testid="product-pod"]')
for item in items:
if required_text in item.text:
description = item.querySelector(
'span[data-testid="attribute-product-label"]'
)
if description and description.text not in unique_item_text:
unique_item_text.append(description.text)
print("* " + description.text)
price = item.querySelector('[class*="sm:sui-text-4xl"]')
if price:
price_text = "$%s" % price.text
print(" (" + price_text + ")")
1 change: 0 additions & 1 deletion examples/cdp_mode/raw_mobile_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ async def main():
driver.stop()

if __name__ == "__main__":
# Call an async function with awaited methods
loop = asyncio.new_event_loop()
with decorators.print_runtime("raw_mobile_async.py"):
loop.run_until_complete(main())
4 changes: 2 additions & 2 deletions examples/cdp_mode/raw_priceline.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from seleniumbase import SB

with SB(uc=True, test=True, locale="en", ad_block=True) as sb:
with SB(uc=True, test=True, locale="en", incognito=True) as sb:
url = "https://www.priceline.com"
sb.activate_cdp_mode(url)
sb.sleep(2.5)
sb.click('input[name="endLocation"]')
sb.sleep(1.2)
location = "Portland, Oregon, US"
location = "Portland, OR"
selection = "Oregon, United States" # (Dropdown option)
sb.press_keys('input[name="endLocation"]', location)
sb.sleep(1.5)
Expand Down
26 changes: 26 additions & 0 deletions examples/cdp_mode/raw_softpedia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
url = "https://www.softpedia.com/"
sb.activate_cdp_mode(url)
search_box = 'input[name="search_term"]'
search = "3D Model Lab"
sb.click(search_box)
sb.press_keys(search_box, search + "\n")
sb.sleep(2)
sb.remove_elements("#adcontainer1")
sb.sleep(2.5)
print('*** Softpedia Search for "%s":' % search)
links = []
item_container = 'div[style="min-height:100px;"]'
sb.wait_for_element(item_container)
items = sb.find_elements(item_container)
for item in items:
result = item.querySelector("h4 a")
links.append(result.get_attribute("href"))
print("* " + result.text)
print(item.querySelector("p").get_attribute("title"))
for link in links:
sb.open(link)
sb.remove_elements("div.ad")
sb.sleep(2)
36 changes: 30 additions & 6 deletions help_docs/syntax_formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
<li><a href="#sb_sf_21"><strong>21. SeleniumBase SB (Python context manager)</strong></a></li>
<li><a href="#sb_sf_22"><strong>22. The driver manager (via context manager)</strong></a></li>
<li><a href="#sb_sf_23"><strong>23. The driver manager (via direct import)</strong></a></li>
<li><a href="#sb_sf_24"><strong>24. CDP driver (async/await API. No Selenium)</strong></a></li>
<li><a href="#sb_sf_25"><strong>25. CDP driver (SB CDP Sync API. No Selenium)</strong></a></li>
<li><a href="#sb_sf_24"><strong>24. Pure CDP Mode (Async API. No Selenium)</strong></a></li>
<li><a href="#sb_sf_25"><strong>25. Pure CDP Mode (Sync API. No Selenium)</strong></a></li>
</ul>
</blockquote>

Expand Down Expand Up @@ -1020,9 +1020,9 @@ The ``Driver()`` manager format can be used as a drop-in replacement for virtual
When using the ``Driver()`` format, you may need to activate a Virtual Display on your own if you want to run headed tests in a headless Linux environment. (See https://github.com/mdmintz/sbVirtualDisplay for details.) One such example of this is using an authenticated proxy, which is configured via a Chrome extension that is generated at runtime. (Note that regular headless mode in Chrome doesn't support extensions.)

<a id="sb_sf_24"></a>
<h2><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> 24. CDP driver (async/await API. No Selenium)</h2>
<h2><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> 24. Pure CDP Mode (Async API. No Selenium)</h2>

This format provides a pure CDP way of using SeleniumBase (without Selenium or a test runner). The async/await API is used. Here's an example:
This format provides a pure CDP way of using SeleniumBase (without Selenium/WebDriver or a test runner). The <code>async</code>/<code>await</code> API is used. Here's an example:

```python
import asyncio
Expand Down Expand Up @@ -1053,9 +1053,33 @@ if __name__ == "__main__":
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_basic_async.py">examples/cdp_mode/raw_basic_async.py</a> for the test.)

<a id="sb_sf_25"></a>
<h2><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> 25. CDP driver (SB CDP Sync API. No Selenium)</h2>
<h2><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> 25. Pure CDP Mode (Sync API. No Selenium)</h2>

This format provides a pure CDP way of using SeleniumBase (without Selenium/WebDriver or a test runner). The expanded SB CDP Sync API is used. Here's an example:
This format provides a pure CDP way of using SeleniumBase (without Selenium/WebDriver or a test runner). The expanded <code>sb_cdp</code> Sync API is used. Here's an example:

```python
from seleniumbase import sb_cdp

url = "https://seleniumbase.io/simple/login"
sb = sb_cdp.Chrome(url)
sb.type("#username", "demo_user")
sb.type("#password", "secret_pass")
sb.click('a:contains("Sign in")')
sb.assert_exact_text("Welcome!", "h1")
sb.assert_element("img#image1")
sb.highlight("#image1")
top_nav = sb.find_element("div.topnav")
links = top_nav.query_selector_all("a")
for nav_item in links:
print(nav_item.text)
sb.click_link("Sign out")
sb.assert_text("signed out", "#top_message")
sb.driver.stop()
```

(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_basic_cdp.py">examples/cdp_mode/raw_basic_cdp.py</a> for the test.)

Here's a Pure CDP Mode example that bypasses bot-detection to scrape data from a website:

```python
from seleniumbase import sb_cdp
Expand Down
2 changes: 1 addition & 1 deletion mkdocs_build/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Minimum Python version: 3.10 (for generating docs only)

regex>=2025.11.3
pymdown-extensions>=10.18
pymdown-extensions>=10.19
pipdeptree>=2.30.0
python-dateutil>=2.8.2
Markdown==3.10
Expand Down
2 changes: 1 addition & 1 deletion seleniumbase/__version__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# seleniumbase package
__version__ = "4.45.1"
__version__ = "4.45.2"
8 changes: 8 additions & 0 deletions seleniumbase/core/browser_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -2769,6 +2769,9 @@ def _set_chrome_options(
included_disabled_features.append("SidePanelPinning")
included_disabled_features.append("UserAgentClientHint")
included_disabled_features.append("DisableLoadExtensionCommandLineSwitch")
included_disabled_features.append("Bluetooth")
included_disabled_features.append("WebBluetooth")
included_disabled_features.append("UnifiedWebBluetooth")
included_disabled_features.append("WebAuthentication")
included_disabled_features.append("PasskeyAuth")
for item in extra_disabled_features:
Expand Down Expand Up @@ -4782,6 +4785,11 @@ def get_local_driver(
included_disabled_features.append(
"DisableLoadExtensionCommandLineSwitch"
)
included_disabled_features.append("Bluetooth")
included_disabled_features.append("WebBluetooth")
included_disabled_features.append("UnifiedWebBluetooth")
included_disabled_features.append("WebAuthentication")
included_disabled_features.append("PasskeyAuth")
for item in extra_disabled_features:
if item not in included_disabled_features:
included_disabled_features.append(item)
Expand Down
6 changes: 5 additions & 1 deletion seleniumbase/core/sb_cdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,13 @@ def get_rd_url(self):
and also applies nest-asyncio for nested event loops so
that SeleniumBase methods can be called from Playwright
without encountering event loop error messages such as:
Cannot run the event loop while another loop is running."""
Cannot run the event loop while another loop is running.
Also sets an environment variable to hide this warning:
Deprecation: "url.parse() behavior is not standardized".
(github.com/microsoft/playwright-python/issues/3016)"""
import nest_asyncio
nest_asyncio.apply()
os.environ["NODE_NO_WARNINGS"] = "1"
driver = self.driver
if hasattr(driver, "cdp_base"):
driver = driver.cdp_base
Expand Down
4 changes: 2 additions & 2 deletions seleniumbase/undetected/cdp_driver/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ async def start(self=None) -> Browser:
""" % (dashes, message, dashes)
)
self.connection = Connection(
self.info.webSocketDebuggerUrl, _owner=self
self.info.webSocketDebuggerUrl, browser=self
)
if self.config.autodiscover_targets:
logger.info("Enabling autodiscover targets")
Expand Down Expand Up @@ -807,7 +807,7 @@ async def update_targets(self):
f"/{t.target_id}"
),
target=t,
_owner=self,
browser=self,
)
)
await asyncio.sleep(0)
Expand Down
12 changes: 0 additions & 12 deletions seleniumbase/undetected/cdp_driver/cdp_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,18 +719,6 @@ def start_sync(*args, **kwargs) -> Browser:
loop = kwargs["loop"]
else:
loop = asyncio.new_event_loop()
if "user_data_dir" in kwargs and kwargs["user_data_dir"]:
headless = False
if "headless" in kwargs:
headless = kwargs["headless"]
decoy_args = kwargs
decoy_args["headless"] = True
driver = loop.run_until_complete(start(**decoy_args))
kwargs["headless"] = headless
kwargs["user_data_dir"] = driver.config.user_data_dir
time.sleep(0.2)
driver.stop() # Due to Chrome-130, must stop & start
time.sleep(0.1)
return loop.run_until_complete(start(*args, **kwargs))


Expand Down
1 change: 1 addition & 0 deletions seleniumbase/undetected/cdp_driver/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ def __call__(self):
"OptimizationTargetPrediction,OptimizationGuideModelDownloading,"
"SidePanelPinning,UserAgentClientHint,PrivacySandboxSettings4,"
"OptimizationHintsFetching,InterestFeedContentSuggestions,"
"Bluetooth,WebBluetooth,UnifiedWebBluetooth,"
"DisableLoadExtensionCommandLineSwitch,"
"WebAuthentication,PasskeyAuth"
]
Expand Down
Loading