From a9c55a4c3b0783057a6d165ed81afd73707a81f9 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 12 Mar 2026 20:08:17 -0400 Subject: [PATCH 1/4] Update CDP Mode --- seleniumbase/core/sb_cdp.py | 104 +++++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 38 deletions(-) diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py index c771a0a6fa4..96e8991c8d7 100644 --- a/seleniumbase/core/sb_cdp.py +++ b/seleniumbase/core/sb_cdp.py @@ -233,11 +233,30 @@ def find_element(self, selector, best_match=False, timeout=None): try: if early_failure: raise Exception("Failed!") - element = self.loop.run_until_complete( - self.page.find( - selector, best_match=best_match, timeout=timeout + if ( + "contains(" not in selector + and not page_utils.is_xpath_selector(selector) + and not re.findall(r"\.\s", selector) + and not re.findall(r"\.$", selector) + and not re.findall(r"#\s", selector) + and not re.findall(r"#$", selector) + and ( + selector in ["html", "body"] + or "[" in selector + or re.findall(r"\.\S", selector) + or re.findall(r"#\S", selector) + or " > " in selector + ) + ): + element = self.loop.run_until_complete( + self.page.select(selector, timeout=timeout) + ) + else: + element = self.loop.run_until_complete( + self.page.find( + selector, best_match=best_match, timeout=timeout + ) ) - ) except Exception: failure = True plural = "s" @@ -375,8 +394,17 @@ def select_all(self, selector, timeout=None): timeout = settings.SMALL_TIMEOUT self.__add_light_pause() selector = self.__convert_to_css_if_xpath(selector) + if not self.is_element_present(selector): + self.sleep(1) + timeout = timeout - 1 + if timeout < 1: + timeout = 1 + try: + self.select(selector, timeout=timeout) + except Exception: + return [] elements = self.loop.run_until_complete( - self.page.select_all(selector, timeout=timeout) + self.page.select_all(selector, timeout=0.1) ) updated_elements = [] for element in elements: @@ -453,7 +481,7 @@ def __click(self, element): result = ( self.loop.run_until_complete(element.click_async()) ) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.2)) return result def __flash(self, element, *args, **kwargs): @@ -506,7 +534,7 @@ def __gui_click(self, element, timeframe=None): if timeframe > 3: timeframe = 3 self.gui_click_x_y(x, y, timeframe=timeframe) - return self.loop.run_until_complete(self.page.wait()) + return self.loop.run_until_complete(self.page.wait(0.2)) def __highlight_overlay(self, element): return ( @@ -517,7 +545,7 @@ def __mouse_click(self, element): result = ( self.loop.run_until_complete(element.mouse_click_async()) ) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.2)) return result def __mouse_click_with_offset_async(self, element, *args, **kwargs): @@ -526,7 +554,7 @@ def __mouse_click_with_offset_async(self, element, *args, **kwargs): element.mouse_click_with_offset_async(*args, **kwargs) ) ) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.2)) return result def __mouse_drag(self, element, destination): @@ -787,14 +815,14 @@ def click(self, selector, timeout=None): else: element.click() # Standard CDP click self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.2)) def click_active_element(self): self.loop.run_until_complete( self.page.evaluate("document.activeElement.click()") ) self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.2)) def click_if_visible(self, selector, timeout=0): if self.is_element_visible(selector): @@ -836,7 +864,7 @@ def click_visible_elements(self, selector, limit=0): click_count += 1 time.sleep(0.044) self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.2)) except Exception: break @@ -849,7 +877,7 @@ def mouse_click(self, selector, timeout=None): element.scroll_into_view() element.mouse_click() self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.2)) def nested_click(self, parent_selector, selector): """ @@ -859,7 +887,7 @@ def nested_click(self, parent_selector, selector): element = self.find_element(parent_selector) element.query_selector(selector).mouse_click() self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.2)) def get_nested_element(self, parent_selector, selector): """(Can be used to find an element inside an iframe)""" @@ -1924,7 +1952,7 @@ def gui_click_element(self, selector, timeframe=0.25): self.__add_light_pause() self.gui_click_x_y(x, y, timeframe=timeframe) self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.2)) def gui_click_with_offset( self, selector, x, y, timeframe=0.25, center=False @@ -1957,7 +1985,7 @@ def click_with_offset(self, selector, x, y, center=False): ) element.click_with_offset(x=x, y=y, center=center) self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.2)) def _on_a_cf_turnstile_page(self, source=None): if not source or len(source) < 400: @@ -1978,7 +2006,7 @@ def _on_a_cf_turnstile_page(self, source=None): return False def _on_an_incapsula_hcaptcha_page(self, *args, **kwargs): - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) if ( self.is_element_visible('iframe[src*="_Incapsula_Resource?"]') or self.is_element_visible("iframe[data-hcaptcha-widget-id]") @@ -1987,7 +2015,7 @@ def _on_an_incapsula_hcaptcha_page(self, *args, **kwargs): return False def _on_a_datadome_slider_page(self, *args, **kwargs): - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) if ( self.is_element_visible( 'body > iframe[src*="/geo.captcha-delivery.com/captcha/"]' @@ -1998,7 +2026,7 @@ def _on_a_datadome_slider_page(self, *args, **kwargs): def _on_a_g_recaptcha_page(self, *args, **kwargs): time.sleep(0.4) # reCAPTCHA may need a moment to appear - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) source = self.get_page_source() if ( ( @@ -2028,7 +2056,7 @@ def __gui_click_recaptcha(self, use_cdp=False): else: return False time.sleep(0.25) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) time.sleep(0.25) with suppress(Exception): element_rect = self.get_element_rect(selector, timeout=0.1) @@ -2077,7 +2105,7 @@ def __gui_slide_datadome_captcha(self): tab = self.get_active_tab() self.open_new_tab(url=src) time.sleep(0.41) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) time.sleep(0.25) x1, y1 = self.get_gui_element_center("div.slider") x2, y2 = self.get_gui_element_center("div.sliderTarget") @@ -2085,7 +2113,7 @@ def __gui_slide_datadome_captcha(self): self.switch_to_tab(tab) self.gui_drag_drop_points(x1, y1, x2, y2, timeframe=0.55) time.sleep(0.25) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.2)) time.sleep(0.15) return True @@ -2103,7 +2131,7 @@ def __cdp_click_incapsula_hcaptcha(self): else: return False time.sleep(0.05) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) time.sleep(0.05) x_offset = 30 y_offset = 36 @@ -2130,7 +2158,7 @@ def __cdp_click_incapsula_hcaptcha(self): # Wait a moment for the click to succeed time.sleep(0.75) self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) if "--debug" in sys.argv: print(" hCaptcha was clicked!") return True @@ -2152,7 +2180,7 @@ def gui_click_captcha(self): def __click_captcha(self, use_cdp=False): """Uses PyAutoGUI unless use_cdp == True""" self.sleep(0.075) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) self.sleep(0.025) source = self.get_page_source() if self._on_a_cf_turnstile_page(source): @@ -2244,7 +2272,7 @@ def __click_captcha(self, use_cdp=False): ) with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(script)) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) elif ( self.is_element_present("form") and ( @@ -2264,7 +2292,7 @@ def __click_captcha(self, use_cdp=False): ) with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(script)) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) elif ( self.is_element_present( 'form [id*="turnstile"] div:not([class])' @@ -2287,7 +2315,7 @@ def __click_captcha(self, use_cdp=False): ) with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(script)) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) elif ( self.is_element_present( '[style*="text-align: center;"] div:not([class])' @@ -2304,7 +2332,7 @@ def __click_captcha(self, use_cdp=False): ) with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(script)) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) with suppress(Exception): time.sleep(0.05) element_rect = self.get_gui_element_rect(selector, timeout=1) @@ -2407,7 +2435,7 @@ def gui_drag_drop_points(self, x1, y1, x2, y2, timeframe=0.35): x1, y1, x2, y2, timeframe=timeframe, uc_lock=False ) self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.2)) def gui_drag_and_drop(self, drag_selector, drop_selector, timeframe=0.35): """Use PyAutoGUI to drag-and-drop from one selector to another. @@ -2514,7 +2542,7 @@ def gui_hover_element(self, selector, timeframe=0.25): self.bring_active_window_to_front() self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False) self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) def hover_element(self, selector, timeframe=0.25): element = self.select(selector) @@ -3077,33 +3105,33 @@ def assert_not_in(self, first, second): def scroll_into_view(self, selector): self.find_element(selector).scroll_into_view() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) def scroll_to_y(self, y): y = int(y) js_code = "window.scrollTo(0, %s);" % y with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(js_code)) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) def scroll_by_y(self, y): y = int(y) js_code = "window.scrollBy(0, %s);" % y with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(js_code)) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) def scroll_to_top(self): js_code = "window.scrollTo(0, 0);" with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(js_code)) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) def scroll_to_bottom(self): js_code = "window.scrollTo(0, 10000);" with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(js_code)) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) def scroll_up(self, amount=25): """Scrolls up as a percentage of the page.""" @@ -3112,7 +3140,7 @@ def scroll_up(self, amount=25): except Exception: amount = self.get_window_size()["height"] * amount / 100 self.execute_script("window.scrollBy(0, -%s);" % amount) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) def scroll_down(self, amount=25): """Scrolls down as a percentage of the page.""" @@ -3121,7 +3149,7 @@ def scroll_down(self, amount=25): except Exception: amount = self.get_window_size()["height"] * amount / 100 self.execute_script("window.scrollBy(0, %s);" % amount) - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.wait(0.1)) def save_page_source(self, name, folder=None): from seleniumbase.core import log_helper From 392b74379b3a31024d446884bdf6ae9ac5686960 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 12 Mar 2026 20:09:02 -0400 Subject: [PATCH 2/4] Update CDP Mode examples --- examples/cdp_mode/raw_cf.py | 13 ++++-- examples/cdp_mode/raw_nevada_search.py | 2 +- examples/cdp_mode/raw_pixelscan.py | 7 ++-- examples/cdp_mode/raw_priceline.py | 49 +++++++++++++++------- examples/cdp_mode/raw_totalwine.py | 4 +- examples/cdp_mode/raw_united.py | 10 +++-- examples/cdp_mode/raw_zoro.py | 2 +- examples/presenter/uc_presentation_4.py | 54 ++++++++++++++++--------- 8 files changed, 92 insertions(+), 49 deletions(-) diff --git a/examples/cdp_mode/raw_cf.py b/examples/cdp_mode/raw_cf.py index 05a921608d5..452c6958a9b 100644 --- a/examples/cdp_mode/raw_cf.py +++ b/examples/cdp_mode/raw_cf.py @@ -1,16 +1,23 @@ -"""Using CDP Mode with PyAutoGUI to bypass CAPTCHAs.""" +"""Using CDP Mode to bypass CAPTCHAs in different ways.""" 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.uc_gui_handle_captcha() # PyAutoGUI press Tab and Spacebar + sb.uc_gui_handle_captcha() # PyAutoGUI Tabs + Spacebar sb.sleep(3) with SB(uc=True, test=True, 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.uc_gui_click_captcha() # PyAutoGUI mouse click + sb.sleep(3) + +with SB(uc=True, test=True, guest=True) as sb: + url = "https://www.cloudflare.com/login" + sb.activate_cdp_mode(url) + sb.sleep(4) + sb.solve_captcha() # CDP Input.dispatchMouseEvent sb.sleep(3) diff --git a/examples/cdp_mode/raw_nevada_search.py b/examples/cdp_mode/raw_nevada_search.py index eb5c063789f..635b549d2d5 100644 --- a/examples/cdp_mode/raw_nevada_search.py +++ b/examples/cdp_mode/raw_nevada_search.py @@ -1,7 +1,7 @@ """Business Entity Search / Bypasses hCaptcha.""" from seleniumbase import SB -with SB(uc=True, test=True, guest=True) as sb: +with SB(uc=True, test=True) as sb: url = "https://www.nvsilverflume.gov/home" sb.activate_cdp_mode(url) sb.sleep(3) diff --git a/examples/cdp_mode/raw_pixelscan.py b/examples/cdp_mode/raw_pixelscan.py index 513820de431..660eec0e642 100644 --- a/examples/cdp_mode/raw_pixelscan.py +++ b/examples/cdp_mode/raw_pixelscan.py @@ -1,18 +1,17 @@ from seleniumbase import SB -with SB(uc=True, test=True, incognito=True) as sb: +with SB(uc=True, test=True, incognito=True, ad_block=True) as sb: url = "https://pixelscan.net/fingerprint-check" sb.activate_cdp_mode(url) - sb.remove_element("#headerBanner") sb.wait_for_element("pxlscn-dynamic-ad") sb.sleep(0.5) sb.remove_elements("pxlscn-dynamic-ad") sb.sleep(2) sb.assert_text("No masking detected", "pxlscn-fingerprint-masking") sb.assert_text("No automated behavior", "pxlscn-bot-detection") - sb.cdp.highlight('span:contains("is consistent")') - sb.sleep(1) sb.cdp.highlight("pxlscn-fingerprint-masking p") sb.sleep(1) sb.cdp.highlight("pxlscn-bot-detection p") + sb.sleep(1) + sb.cdp.highlight('span.status-success') sb.sleep(2) diff --git a/examples/cdp_mode/raw_priceline.py b/examples/cdp_mode/raw_priceline.py index fb76ff41dae..a951f74b03e 100644 --- a/examples/cdp_mode/raw_priceline.py +++ b/examples/cdp_mode/raw_priceline.py @@ -1,23 +1,29 @@ +"""Priceline does a lot of A/B testing. Selectors change frequently.""" from seleniumbase import SB -with SB(uc=True, test=True, locale="en", guest=True, ad_block=True) as sb: +with SB(uc=True, test=True, locale="en", guest=True, pls="none") as sb: url = "https://www.priceline.com" sb.activate_cdp_mode(url) - sb.sleep(1.8) - sb.click('input[name="endLocation"]') + sb.sleep(2.6) + input_selector = '[name="endLocation"]' + if not sb.is_element_present(input_selector): + input_selector = "div.location-input input" + sb.click(input_selector) sb.sleep(1.2) location = "Portland, OR" selection = "Oregon, United States" # (Dropdown option) - sb.press_keys('input[name="endLocation"]', location) - sb.sleep(0.5) - sb.click_if_visible('input[name="endLocation"]') - sb.sleep(0.5) + sb.press_keys(input_selector, location) + sb.sleep(0.6) sb.click(selection) + sb.sleep(0.4) sb.scroll_down(25) - sb.click_if_visible('button[aria-label="Dismiss calendar"]') - sb.click_if_visible("div.sidebar-iframe-close") - sb.click_if_visible('div[aria-label="Close Modal"]') - sb.click('button[data-testid="HOTELS_SUBMIT_BUTTON"]') + sb.sleep(0.4) + calendar_close = 'button[aria-label="Dismiss calendar"]' + if not sb.is_element_visible(calendar_close): + calendar_close = '[data-mode="range"] span.px-1' + sb.click(calendar_close) + sb.sleep(0.6) + sb.click('form button[type="submit"]') sb.sleep(4.8) if len(sb.cdp.get_tabs()) > 1: sb.cdp.close_active_tab() @@ -27,13 +33,24 @@ for y in range(1, 9): sb.scroll_to_y(y * 400) sb.sleep(0.5) - hotel_names = sb.find_elements('a[data-autobot-element-id*="HOTEL_NAME"]') - if sb.is_element_visible('[font-size="4,,,5"]'): - hotel_prices = sb.find_elements('[font-size="4,,,5"]') - else: + hotel_names = sb.find_elements('h3 div[class*="TitleName"]') + if not hotel_names: + hotel_names = sb.find_elements( + 'a[data-autobot-element-id*="HOTEL_NAME"]' + ) + price_selector = '[class*="PriceWrap"] .relative > .items-center' + if sb.is_element_visible(price_selector): + hotel_prices = sb.find_elements(price_selector) + elif sb.is_element_present( + '[font-size="12px"] + [font-size="20px"]' + ): hotel_prices = sb.find_elements( '[font-size="12px"] + [font-size="20px"]' ) + else: + hotel_prices = sb.find_elements( + 'span.text-priceSuper-heading4 + div > span' + ) print("Priceline Hotels in %s:" % location) print(sb.get_text('[data-testid="POPOVER-DATE-PICKER"]')) if len(hotel_names) == 0: @@ -43,4 +60,6 @@ if hotel_prices[i] and hotel_prices[i].text: count += 1 hotel_price = "$" + hotel_prices[i].text + if hotel_price.startswith("$$ "): + hotel_price = hotel_price.replace("$$ ", "$") print("* %s: %s => %s" % (count, hotel.text, hotel_price)) diff --git a/examples/cdp_mode/raw_totalwine.py b/examples/cdp_mode/raw_totalwine.py index e1afd89976a..2da380d47fb 100644 --- a/examples/cdp_mode/raw_totalwine.py +++ b/examples/cdp_mode/raw_totalwine.py @@ -18,13 +18,15 @@ sb.sleep(1.2) sb.press_keys(search_box, search) sb.sleep(0.6) + sb.click_if_visible('button[aria-label="Close modal"]') sb.click('button[data-at="header-search-button"]') sb.sleep(1.8) + sb.click_if_visible('button[aria-label="Close modal"]') sb.click('img[data-at="product-search-productimage"]') sb.sleep(2.2) print('*** Total Wine Search for "%s":' % search) print(sb.get_text('h1[data-at="product-name-title"]')) - print(sb.get_text('span[data-at="product-mix6price-text"]')) + print(sb.get_text('span[data-at="product-mixCaseprice-text"]')) print("Product Highlights:") print(sb.get_text('p[class*="productInformationReview"]')) print("Product Details:") diff --git a/examples/cdp_mode/raw_united.py b/examples/cdp_mode/raw_united.py index 3a8aecea29a..a6441bfe616 100644 --- a/examples/cdp_mode/raw_united.py +++ b/examples/cdp_mode/raw_united.py @@ -3,11 +3,13 @@ with SB(uc=True, test=True, locale="en", ad_block=True) as sb: url = "https://www.united.com/en/us" sb.activate_cdp_mode(url) - sb.sleep(2.6) + sb.sleep(3.5) origin_input = 'input[placeholder="Origin"]' - origin = "New York, NY" + origin = "JFK" destination_input = 'input[placeholder="Destination"]' - destination = "Orlando, FL" + destination = "MCO" + sb.wait_for_element(origin_input, timeout=20) + sb.sleep(0.5) sb.click(origin_input) sb.sleep(0.5) sb.type(origin_input, origin) @@ -39,7 +41,7 @@ part_3 = flight.text.split(" Destination")[-1].split(" Aircraft")[0] parts = "%s - %s %s" % (part_1, part_2, part_3) print("* " + parts) - for category in ["ECONOMY", "ECONOMY-UNRESTRICTED"]: + for category in ["ECONOMY"]: prices = sb.find_elements('[aria-describedby="%s"]' % category) full_prices = [] for item in prices: diff --git a/examples/cdp_mode/raw_zoro.py b/examples/cdp_mode/raw_zoro.py index 0f0beecc311..4e71a7672ba 100644 --- a/examples/cdp_mode/raw_zoro.py +++ b/examples/cdp_mode/raw_zoro.py @@ -24,7 +24,7 @@ items = sb.find_elements('[data-za="search-product-card"]') for item in items: if required_text in item.text: - description = item.querySelector('[data-za="product-title"]') + description = item.querySelector("h2") if description and description.text not in unique_item_text: unique_item_text.append(description.text) print("* " + description.text) diff --git a/examples/presenter/uc_presentation_4.py b/examples/presenter/uc_presentation_4.py index d4d0e888a2f..58fd2e8fa93 100644 --- a/examples/presenter/uc_presentation_4.py +++ b/examples/presenter/uc_presentation_4.py @@ -769,26 +769,29 @@ def test_presentation_4(self): ) self.begin_presentation(filename="uc_presentation.html") - with SB( - uc=True, test=True, locale="en", guest=True, ad_block=True - ) as sb: + with SB(uc=True, test=True, locale="en", guest=True, pls="none") as sb: url = "https://www.priceline.com" sb.activate_cdp_mode(url) - sb.sleep(1.8) - sb.click('input[name="endLocation"]') + sb.sleep(2.6) + input_selector = '[name="endLocation"]' + if not sb.is_element_present(input_selector): + input_selector = "div.location-input input" + sb.click(input_selector) 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(0.5) - sb.click_if_visible('input[name="endLocation"]') - sb.sleep(0.5) + sb.press_keys(input_selector, location) + sb.sleep(0.6) sb.click(selection) + sb.sleep(0.4) sb.scroll_down(25) - sb.click_if_visible('button[aria-label="Dismiss calendar"]') - sb.click_if_visible("div.sidebar-iframe-close") - sb.click_if_visible('div[aria-label="Close Modal"]') - sb.click('button[data-testid="HOTELS_SUBMIT_BUTTON"]') + sb.sleep(0.4) + calendar_close = 'button[aria-label="Dismiss calendar"]' + if not sb.is_element_visible(calendar_close): + calendar_close = '[data-mode="range"] span.px-1' + sb.click(calendar_close) + sb.sleep(0.6) + sb.click('form button[type="submit"]') sb.sleep(4.8) if len(sb.cdp.get_tabs()) > 1: sb.cdp.close_active_tab() @@ -798,15 +801,24 @@ def test_presentation_4(self): for y in range(1, 9): sb.scroll_to_y(y * 400) sb.sleep(0.5) - hotel_names = sb.find_elements( - 'a[data-autobot-element-id*="HOTEL_NAME"]' - ) - if sb.is_element_visible('[font-size="4,,,5"]'): - hotel_prices = sb.find_elements('[font-size="4,,,5"]') - else: + hotel_names = sb.find_elements('h3 div[class*="TitleName"]') + if not hotel_names: + hotel_names = sb.find_elements( + 'a[data-autobot-element-id*="HOTEL_NAME"]' + ) + price_selector = '[class*="PriceWrap"] .relative > .items-center' + if sb.is_element_visible(price_selector): + hotel_prices = sb.find_elements(price_selector) + elif sb.is_element_present( + '[font-size="12px"] + [font-size="20px"]' + ): hotel_prices = sb.find_elements( '[font-size="12px"] + [font-size="20px"]' ) + else: + hotel_prices = sb.find_elements( + 'span.text-priceSuper-heading4 + div > span' + ) print("Priceline Hotels in %s:" % location) print(sb.get_text('[data-testid="POPOVER-DATE-PICKER"]')) if len(hotel_names) == 0: @@ -816,6 +828,8 @@ def test_presentation_4(self): if hotel_prices[i] and hotel_prices[i].text: count += 1 hotel_price = "$" + hotel_prices[i].text + if hotel_price.startswith("$$ "): + hotel_price = hotel_price.replace("$$ ", "$") print("* %s: %s => %s" % (count, hotel.text, hotel_price)) self.create_presentation(theme="serif", transition="none") From e7661ded36eef509f12baf6b4700945f5858a423 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 12 Mar 2026 20:09:19 -0400 Subject: [PATCH 3/4] Refresh Python dependencies --- requirements.txt | 4 ++-- setup.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 45ba2874714..487538a9f78 100755 --- a/requirements.txt +++ b/requirements.txt @@ -9,9 +9,9 @@ exceptiongroup>=1.3.1 websockets~=15.0.1;python_version<"3.10" websockets>=16.0;python_version>="3.10" filelock~=3.19.1;python_version<"3.10" -filelock>=3.25.1;python_version>="3.10" +filelock>=3.25.2;python_version>="3.10" fasteners>=0.20 -mycdp>=1.3.3 +mycdp>=1.3.4 pynose>=1.5.5 platformdirs~=4.4.0;python_version<"3.10" platformdirs>=4.9.4;python_version>="3.10" diff --git a/setup.py b/setup.py index a58a86e11a5..314ad27d530 100755 --- a/setup.py +++ b/setup.py @@ -157,9 +157,9 @@ 'websockets~=15.0.1;python_version<"3.10"', 'websockets>=16.0;python_version>="3.10"', 'filelock~=3.19.1;python_version<"3.10"', - 'filelock>=3.25.1;python_version>="3.10"', + 'filelock>=3.25.2;python_version>="3.10"', 'fasteners>=0.20', - 'mycdp>=1.3.3', + 'mycdp>=1.3.4', 'pynose>=1.5.5', 'platformdirs~=4.4.0;python_version<"3.10"', 'platformdirs>=4.9.4;python_version>="3.10"', From c0fad2626605749005b09f994bb065306e363aa5 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 12 Mar 2026 20:09:33 -0400 Subject: [PATCH 4/4] Version 4.47.3 --- seleniumbase/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index d3618996606..1b1e219acec 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.47.2" +__version__ = "4.47.3"