diff --git a/README.md b/README.md
index 95a112f7473..241e0190770 100755
--- a/README.md
+++ b/README.md
@@ -54,13 +54,15 @@
SeleniumBase is the professional toolkit for web automation activities. Built for testing websites, bypassing CAPTCHAs, enhancing productivity, completing tasks, and scaling your business.
+ -------- đ Learn from [**over 200 examples** in the **SeleniumBase/examples/** folder](https://github.com/seleniumbase/SeleniumBase/tree/master/examples). đ¤ Note that SeleniumBase UC Mode (Stealth Mode) has its own ReadMe. -âšī¸ Scripts can be called viapython
, although some Syntax Formats expect pytest (a Python unit-testing framework included with SeleniumBase that can discover & collect tests automatically).
+âšī¸ Scripts can be called via python
, although some Syntax Formats expect pytest (a Python unit-testing framework included with SeleniumBase that can discover, collect, and run tests automatically).
đ Here's my_first_test.py, which tests login, shopping, and checkout:
diff --git a/examples/presenter/uc_presentation.py b/examples/presenter/uc_presentation.py index ba6247cdc0c..ae5ff4dc2f2 100644 --- a/examples/presenter/uc_presentation.py +++ b/examples/presenter/uc_presentation.py @@ -30,12 +30,12 @@ def test_presentation(self): self.get_new_driver(undetectable=True) url = "https://gitlab.com/users/sign_in" try: - self.driver.uc_open_with_reconnect(url, reconnect_time=3) + self.uc_open_with_reconnect(url, reconnect_time=3) try: self.assert_text("Username", '[for="user_login"]', timeout=3) self.post_message("SeleniumBase wasn't detected", duration=4) except Exception: - self.driver.uc_open_with_reconnect(url, reconnect_time=4) + self.uc_open_with_reconnect(url, reconnect_time=4) self.assert_text("Username", '[for="user_login"]', timeout=3) self.post_message("SeleniumBase wasn't detected", duration=4) finally: @@ -249,8 +249,7 @@ def test_presentation(self): code=( "uc_gui_handle_captcha()
sb.uc_gui_handle_captcha()
uc_gui_click_captcha()
sb.uc_gui_click_captcha()
sb.uc_open_with_reconnect(url)
"
"uc_gui_handle_captcha()
"
- " or uc_gui_click_captcha()
"
+ "sb.uc_gui_handle_captcha()
"
+ " or sb.uc_gui_click_captcha()
"
" to bypass CAPTCHAs as needed.uc_click(selector)
"
+ "sb.uc_click(selector)
"
"đ¤
" - '
uc_gui_click_captcha(frame="iframe", retry=False,'
+ 'sb.uc_gui_click_captcha(frame="iframe", retry=False,'
' blind=True )
'
'Set the third arg, `blind`, to `True` to force a retry'
' (if the first click failed) by clicking at the last known'
diff --git a/examples/raw_ahrefs.py b/examples/raw_ahrefs.py
index f09cf78d322..55898298458 100644
--- a/examples/raw_ahrefs.py
+++ b/examples/raw_ahrefs.py
@@ -4,12 +4,12 @@
url = "https://ahrefs.com/website-authority-checker"
input_field = 'input[placeholder="Enter domain"]'
submit_button = 'span:contains("Check Authority")'
- sb.uc_open_with_reconnect(url, 2) # The bot-check is later
+ sb.uc_open_with_reconnect(url) # The bot-check is later
sb.type(input_field, "github.com/seleniumbase/SeleniumBase")
sb.reconnect(0.1)
sb.uc_click(submit_button, reconnect_time=4)
sb.uc_gui_click_captcha()
- sb.wait_for_text_not_visible("Checking", timeout=10)
+ sb.wait_for_text_not_visible("Checking", timeout=12)
sb.highlight('p:contains("github.com/seleniumbase/SeleniumBase")')
sb.highlight('a:contains("Top 100 backlinks")')
sb.set_messenger_theme(location="bottom_center")
diff --git a/examples/raw_cf.py b/examples/raw_cf.py
index b8a79149773..58df9df8973 100644
--- a/examples/raw_cf.py
+++ b/examples/raw_cf.py
@@ -1,14 +1,14 @@
"""SB Manager using UC Mode & PyAutoGUI for bypassing CAPTCHAs."""
from seleniumbase import SB
-with SB(uc=True, test=True) as sb:
+with SB(uc=True, test=True, locale_code="en") as sb:
url = "https://www.cloudflare.com/login"
- sb.uc_open_with_reconnect(url, 5)
+ sb.uc_open_with_reconnect(url, 5.5)
sb.uc_gui_handle_captcha() # PyAutoGUI press Tab and Spacebar
sb.sleep(2.5)
-with SB(uc=True, test=True) as sb:
+with SB(uc=True, test=True, locale_code="en") as sb:
url = "https://www.cloudflare.com/login"
- sb.uc_open_with_reconnect(url, 5)
+ sb.uc_open_with_reconnect(url, 5.5)
sb.uc_gui_click_captcha() # PyAutoGUI click. (Linux needs it)
sb.sleep(2.5)
diff --git a/examples/raw_order_tickets.py b/examples/raw_order_tickets.py
index 58017837a15..caabfd1c036 100644
--- a/examples/raw_order_tickets.py
+++ b/examples/raw_order_tickets.py
@@ -1,7 +1,8 @@
from seleniumbase import SB
with SB(uc=True, test=True, ad_block=True) as sb:
- url = "https://www.thaiticketmajor.com/concert/"
+ url = "https://www.thaiticketmajor.com/concert/#"
sb.uc_open_with_reconnect(url, 6.111)
sb.uc_click("button.btn-signin", 4.1)
sb.uc_gui_click_captcha()
+ sb.sleep(2)
diff --git a/examples/raw_pixelscan.py b/examples/raw_pixelscan.py
index 1868ddff3db..03a140019f6 100644
--- a/examples/raw_pixelscan.py
+++ b/examples/raw_pixelscan.py
@@ -3,12 +3,14 @@
with SB(uc=True, incognito=True, test=True) as sb:
sb.driver.uc_open_with_reconnect("https://pixelscan.net/", 10)
sb.remove_elements("jdiv") # Remove chat widgets
- sb.assert_text("No automation framework detected", "pxlscn-bot-detection")
- not_masking = "You are not masking your fingerprint"
- sb.assert_text(not_masking, "pxlscn-fingerprint-masking")
+ no_automation_detected = "No automation framework detected"
+ sb.assert_text(no_automation_detected, "pxlscn-bot-detection")
+ not_masking_text = "You are not masking your fingerprint"
+ sb.assert_text(not_masking_text, "pxlscn-fingerprint-masking")
sb.highlight("span.text-success", loops=8)
sb.sleep(1)
- sb.highlight("pxlscn-fingerprint-masking div", loops=9, scroll=False)
+ fingerprint_masking_div = "pxlscn-fingerprint-masking div"
+ sb.highlight(fingerprint_masking_div, loops=9, scroll=False)
sb.sleep(1)
- sb.highlight("div.bot-detection-context", loops=10, scroll=False)
+ sb.highlight(".bot-detection-context", loops=10, scroll=False)
sb.sleep(2)
diff --git a/help_docs/thank_you.md b/help_docs/thank_you.md
index 87a0d36fe6f..ec860135f81 100644
--- a/help_docs/thank_you.md
+++ b/help_docs/thank_you.md
@@ -1,31 +1,36 @@
-[](https://github.com/seleniumbase/SeleniumBase/blob/master/README.md)
+
-### Thank you for flying with SeleniumBase! đĻ
+
+đŦđĢ Thank you for flying with SeleniumBase! đĻ
đ
--------
-SeleniumBase Playlist on YouTube:
+đŠī¸ SeleniumBase Playlist on YouTube:
+
+
-
+đŠī¸ SeleniumBase GitHub Home Page:
-SeleniumBase GitHub Repo Link:
+
-
+đŠī¸ SeleniumBase Discord Channel:
-SeleniumBase Gitter Chat Link:
+
-
+đŠī¸ SeleniumBase Gitter Chat:
+
+
Other Social Media Links:
-
-
-
-
+
--------
-
+
diff --git a/help_docs/uc_mode.md b/help_docs/uc_mode.md
index 2c8c1915bf7..2cb24a48f65 100644
--- a/help_docs/uc_mode.md
+++ b/help_docs/uc_mode.md
@@ -99,12 +99,12 @@ with SB(uc=True, test=True, incognito=True, locale_code="en") as sb:
url = "https://ahrefs.com/website-authority-checker"
input_field = 'input[placeholder="Enter domain"]'
submit_button = 'span:contains("Check Authority")'
- sb.uc_open_with_reconnect(url, 2) # The bot-check is later
+ sb.uc_open_with_reconnect(url) # The bot-check is later
sb.type(input_field, "github.com/seleniumbase/SeleniumBase")
sb.reconnect(0.1)
sb.uc_click(submit_button, reconnect_time=4)
sb.uc_gui_click_captcha()
- sb.wait_for_text_not_visible("Checking", timeout=10)
+ sb.wait_for_text_not_visible("Checking", timeout=12)
sb.highlight('p:contains("github.com/seleniumbase/SeleniumBase")')
sb.highlight('a:contains("Top 100 backlinks")')
sb.set_messenger_theme(location="bottom_center")
@@ -158,10 +158,29 @@ The 2nd `print()` should output "Virtual Manager", which means that the automati
* [SeleniumBase/examples/verify_undetected.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/verify_undetected.py)
* [SeleniumBase/examples/raw_bing_captcha.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_bing_captcha.py)
* [SeleniumBase/examples/raw_uc_mode.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_uc_mode.py)
+* [SeleniumBase/examples/raw_cf.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_cf.py)
+
+--------
+
+đ¤ Here's an example where **`incognito=True` is needed for bypassing detection**:
+
* [SeleniumBase/examples/raw_pixelscan.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_pixelscan.py)
+```python
+from seleniumbase import SB
+
+with SB(uc=True, incognito=True, test=True) as sb:
+ sb.driver.uc_open_with_reconnect("https://pixelscan.net/", 10)
+ sb.remove_elements("jdiv") # Remove chat widgets
+ sb.highlight("span.text-success", loops=8)
+ sb.highlight(".bot-detection-context", loops=10, scroll=False)
+ sb.sleep(2)
+```
+
+--------
+
### đ¤ Here are some UC Mode examples that bypass CAPTCHAs when clicking is required:
* [SeleniumBase/examples/raw_pyautogui.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_pyautogui.py)
* [SeleniumBase/examples/raw_turnstile.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_turnstile.py)
@@ -170,6 +189,8 @@ The 2nd `print()` should output "Virtual Manager", which means that the automati
+--------
+
### đ¤ Here are the driver
-specific methods added by SeleniumBase for UC Mode: `--uc` / uc=True
```python
@@ -240,6 +261,8 @@ driver.reconnect("breakpoint")
(Note that while the special UC Mode
breakpoint is active, you can't use Selenium
commands in the browser, and the browser can't detect Selenium
.)
+--------
+
đ¤ On Linux, you may need to use `uc_gui_click_captcha()` to successfully bypass a Cloudflare CAPTCHA. If there's more than one Cloudflare iframe on that website, then put the CSS Selector of an element that's above the iframe as the first arg to `uc_gui_click_captcha()`. This method uses `pyautogui`. In order for `pyautogui` to focus on the correct element, use `xvfb=True` / `--xvfb` to activate a special virtual display on Linux.
đ¤ `uc_gui_click_captcha()` auto-detects the CAPTCHA type before trying to click it. This is a generic method for both CF Turnstile and Google reCAPTCHA. It will use the code from `uc_gui_click_cf()` and `uc_gui_click_rc()` as needed.
@@ -248,6 +271,8 @@ driver.reconnect("breakpoint")
đ¤ `uc_gui_click_rc(frame="iframe", retry=False, blind=False)` is for reCAPTCHA. This may only work a few times before not working anymore... not because Selenium was detected, but because reCAPTCHA uses advanced AI to detect unusual activity, unlike the CF Turnstile, which only uses basic detection.
+--------
+
đ¤ To find out if UC Mode will work at all on a specific site (before adjusting for timing), load your site with the following script:
```python
@@ -259,6 +284,8 @@ with SB(uc=True) as sb:
(If you remain undetected while loading the page and performing manual actions, then you know you can create a working script once you swap the breakpoint with a time and add special methods like sb.uc_click
as needed.)
+--------
+
đ¤ Multithreaded UC Mode:
If you're using pytest
for multithreaded UC Mode (which requires using one of the pytest
[syntax formats](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md)), then all you have to do is set the number of threads when your script runs. (`-n NUM`) Eg:
@@ -343,7 +370,7 @@ The above JS method is used within the SeleniumBase
-As an ethical hacker / cybersecurity researcher who builds bots that bypass CAPTCHAs for sport, the CAPTCHA service that I personally recommend for keeping bots out is Google's reCAPTCHA:
+As an ethical hacker / cybersecurity researcher who builds bots that bypass CAPTCHAs for sport, the CAPTCHA service that I personally recommend for keeping bots out is Google reCAPTCHA:
diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py
index c7124640cc9..0305db40ed2 100755
--- a/seleniumbase/__version__.py
+++ b/seleniumbase/__version__.py
@@ -1,2 +1,2 @@
# seleniumbase package
-__version__ = "4.30.2"
+__version__ = "4.30.3"
diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py
index 9e41ed3d9cd..413aec8d36c 100644
--- a/seleniumbase/core/browser_launcher.py
+++ b/seleniumbase/core/browser_launcher.py
@@ -861,10 +861,19 @@ def _uc_gui_click_captcha(
visible_iframe = False
if (
frame != "iframe"
- and driver.is_element_present('[name*="cf-turnstile-"]')
- and driver.is_element_present("%s div[style]" % frame)
+ and driver.is_element_present(
+ "%s .cf-turnstile-wrapper" % frame
+ )
+ ):
+ frame = "%s .cf-turnstile-wrapper" % frame
+ elif (
+ frame != "iframe"
+ and driver.is_element_present(
+ '%s [name*="cf-turnstile"]' % frame
+ )
+ and driver.is_element_present("%s div" % frame)
):
- frame = "%s div[style]" % frame
+ frame = "%s div" % frame
elif (
driver.is_element_present('[name*="cf-turnstile-"]')
and driver.is_element_present("div.spacer div")
@@ -877,47 +886,21 @@ def _uc_gui_click_captcha(
)
):
frame = '[data-testid*="challenge-"] div'
- elif (
- (
- driver.is_element_present('[name*="cf-turnstile-"]')
- or driver.is_element_present('[id*="cf-turnstile-"]')
- )
- and driver.is_element_present(
- 'form div div[style*="margin"][style*="padding"]'
- )
- ):
- frame = 'form div div[style*="margin"][style*="padding"]'
- elif (
- frame != "iframe"
- and driver.is_element_present(
- "%s .cf-turnstile-wrapper" % frame
- )
+ elif driver.is_element_present(
+ 'form div:not([class]):has(input[name*="cf-turn"])'
):
- frame = "%s .cf-turnstile-wrapper" % frame
+ frame = 'form div:not([class]):has(input[name*="cf-turn"])'
elif (
- frame != "iframe"
- and driver.is_element_present(
- '%s [name*="cf-turnstile"]' % frame
- )
- and driver.is_element_present("%s div" % frame)
+ driver.is_element_present('[src*="/turnstile/"]')
+ and driver.is_element_present("form div:not(:has(*))")
):
- frame = "%s div" % frame
+ frame = "form div:not(:has(*))"
elif driver.is_element_present(".cf-turnstile-wrapper"):
frame = ".cf-turnstile-wrapper"
elif driver.is_element_present(
'[data-callback="onCaptchaSuccess"]'
):
frame = '[data-callback="onCaptchaSuccess"]'
- elif (
- (
- driver.is_element_present('[name*="cf-turnstile-"]')
- or driver.is_element_present('[id*="cf-turnstile-"]')
- )
- and driver.is_element_present(
- 'div > div > [style*="margin"][style*="padding"]'
- )
- ):
- frame = 'div > div > [style*="margin"][style*="padding"]'
else:
return
if driver.is_element_present('form[class*=center]'):
@@ -1100,6 +1083,8 @@ def _uc_gui_handle_captcha(
driver.minimize_window()
driver.set_window_rect(win_x, win_y, width, height)
time.sleep(0.33)
+ tab_up_first = False
+ special_form = False
if ctype == "cf_t":
if (
driver.is_element_present(".cf-turnstile-wrapper iframe")
@@ -1128,26 +1113,18 @@ def _uc_gui_handle_captcha(
)
):
frame = '[data-testid*="challenge-"] div'
- elif (
- (
- driver.is_element_present('[name*="cf-turnstile-"]')
- or driver.is_element_present('[id*="cf-turnstile-"]')
- )
- and driver.is_element_present(
- 'form div div[style*="margin"][style*="padding"]'
- )
+ elif driver.is_element_present(
+ 'form div:not([class]):has(input[name*="cf-turn"])'
):
- frame = 'form div div[style*="margin"][style*="padding"]'
+ frame = 'form div:not([class]):has(input[name*="cf-turn"])'
+ tab_up_first = True
+ special_form = True
elif (
- (
- driver.is_element_present('[name*="cf-turnstile-"]')
- or driver.is_element_present('[id*="cf-turnstile-"]')
- )
- and driver.is_element_present(
- 'div > div > [style*="margin"][style*="padding"]'
- )
+ driver.is_element_present('[src*="/turnstile/"]')
+ and driver.is_element_present("form div:not(:has(*))")
):
- frame = 'div > div > [style*="margin"][style*="padding"]'
+ frame = "form div:not(:has(*))"
+ tab_up_first = True
else:
return
else:
@@ -1172,13 +1149,18 @@ def _uc_gui_handle_captcha(
if ctype == "g_rc":
selector = "span#recaptcha-anchor"
found_checkbox = False
- for i in range(24):
+ if tab_up_first:
+ for i in range(10):
+ pyautogui.hotkey("shift", "tab")
+ time.sleep(0.027)
+ for i in range(34):
pyautogui.press("\t")
- time.sleep(0.02)
+ time.sleep(0.027)
active_element_css = js_utils.get_active_element_css(driver)
if (
active_element_css.startswith(selector)
or active_element_css.endswith(" > div" * 2)
+ or (special_form and active_element_css.endswith(" div"))
):
found_checkbox = True
break
diff --git a/setup.py b/setup.py
index 9c13e687615..968f434b044 100755
--- a/setup.py
+++ b/setup.py
@@ -34,7 +34,7 @@
print("\nERROR! Publishing to PyPI requires Python>=3.9")
sys.exit()
print("\n*** Checking code health with flake8:\n")
- os.system("python -m pip install 'flake8==7.1.0'")
+ os.system("python -m pip install 'flake8==7.1.1'")
flake8_status = os.system("flake8 --exclude=recordings,temp")
if flake8_status != 0:
print("\nERROR! Fix flake8 issues before publishing to PyPI!\n")
@@ -265,7 +265,7 @@
'cryptography==39.0.2;python_version<"3.9"',
'cryptography==43.0.1;python_version>="3.9"',
'cffi==1.15.1;python_version<"3.8"',
- 'cffi==1.17.0;python_version>="3.8"',
+ 'cffi==1.17.1;python_version>="3.8"',
"pycparser==2.22",
],
# pip install -e .[pillow]