Skip to content

Lots of UC Mode improvements, and more #2866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 23, 2024
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
15 changes: 15 additions & 0 deletions examples/raw_antibot_login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""UC Mode has PyAutoGUI methods for CAPTCHA-bypass."""
from seleniumbase import SB

with SB(uc=True, test=True) as sb:
url = "https://seleniumbase.io/antibot/login"
sb.uc_open_with_disconnect(url, 2.15)
sb.uc_gui_write("\t" + "demo_user")
sb.uc_gui_write("\t" + "secret_pass")
sb.uc_gui_press_keys("\t" + " ") # For Single-char keys
sb.sleep(1.5)
sb.uc_gui_press_keys(["\t", "ENTER"]) # Multi-char keys
sb.reconnect(1.8)
sb.assert_text("Welcome!", "h1")
sb.set_messenger_theme(location="bottom_center")
sb.post_message("SeleniumBase wasn't detected!")
11 changes: 11 additions & 0 deletions examples/raw_block.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""If Brotector catches you, Gandalf blocks you!"""
from seleniumbase import SB

with SB(test=True) as sb:
url = "https://seleniumbase.io/hobbit/login"
sb.open(url)
sb.click_if_visible("button")
sb.assert_text("Gandalf blocked you!", "h1")
sb.click("img")
sb.highlight("h1")
sb.sleep(3) # Gandalf: "You Shall Not Pass!"
9 changes: 9 additions & 0 deletions examples/raw_brotector_captcha.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""UC Mode has PyAutoGUI methods for CAPTCHA-bypass."""
from seleniumbase import SB

with SB(uc=True, test=True) as sb:
url = "https://seleniumbase.io/apps/brotector"
sb.uc_open_with_disconnect(url, 2.2)
sb.uc_gui_press_key("\t")
sb.uc_gui_press_key(" ")
sb.reconnect(2.2)
12 changes: 12 additions & 0 deletions examples/raw_detection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""The Brotector CAPTCHA in action."""
from seleniumbase import SB

with SB(test=True) as sb:
sb.open("https://seleniumbase.io/antibot/login")
sb.highlight("h4", loops=6)
sb.type("#username", "demo_user")
sb.type("#password", "secret_pass")
sb.click_if_visible("button span")
sb.highlight("label#pText")
sb.highlight("table#detections")
sb.sleep(4.4) # Add time to read the table
13 changes: 13 additions & 0 deletions examples/raw_hobbit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""UC Mode has PyAutoGUI methods for CAPTCHA-bypass."""
from seleniumbase import SB

with SB(uc=True, test=True) as sb:
url = "https://seleniumbase.io/hobbit/login"
sb.uc_open_with_disconnect(url, 2.2)
sb.uc_gui_press_keys("\t ")
sb.reconnect(1.5)
sb.assert_text("Welcome to Middle Earth!", "h1")
sb.set_messenger_theme(location="bottom_center")
sb.post_message("SeleniumBase wasn't detected!")
sb.click("img")
sb.sleep(5.888) # Cool animation happening now!
19 changes: 19 additions & 0 deletions examples/raw_pyautogui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""
UC Mode now has uc_gui_handle_cf(), which uses PyAutoGUI.
An incomplete UserAgent is used to force CAPTCHA-solving.
"""
import sys
from seleniumbase import SB

agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/126.0.0.0"
if "linux" in sys.platform:
agent = None # Use the default UserAgent

with SB(uc=True, test=True, rtf=True, agent=agent) as sb:
url = "https://www.virtualmanager.com/en/login"
sb.uc_open_with_reconnect(url, 4)
sb.uc_gui_handle_cf() # Ready if needed!
sb.assert_element('input[name*="email"]')
sb.assert_element('input[name*="login"]')
sb.set_messenger_theme(location="bottom_center")
sb.post_message("SeleniumBase wasn't detected!")
20 changes: 14 additions & 6 deletions help_docs/method_summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ self.click_partial_link(partial_link_text, timeout=None)
# Duplicates:
# self.click_partial_link_text(partial_link_text, timeout=None)

self.get_text(selector, by="css selector", timeout=None)
self.get_text(selector="html", by="css selector", timeout=None)

self.get_attribute(selector, attribute, by="css selector", timeout=None, hard_fail=True)

Expand All @@ -127,7 +127,7 @@ self.remove_attributes(selector, attribute, by="css selector")

self.get_property(selector, property, by="css selector", timeout=None)

self.get_text_content(selector, by="css selector", timeout=None)
self.get_text_content(selector="html", by="css selector", timeout=None)

self.get_property_value(selector, property, by="css selector", timeout=None)

Expand Down Expand Up @@ -229,7 +229,7 @@ self.set_window_size(width, height)

self.maximize_window()

self.switch_to_frame(frame, timeout=None)
self.switch_to_frame(frame="iframe", timeout=None)

self.switch_to_default_content()

Expand Down Expand Up @@ -1032,7 +1032,7 @@ driver.get_page_source()

driver.get_title()

driver.switch_to_frame(frame)
driver.switch_to_frame(frame="iframe")

############

Expand All @@ -1046,7 +1046,7 @@ driver.uc_open_with_tab(url) # (New tab with default reconnect_time)

driver.uc_open_with_reconnect(url, reconnect_time=None) # (New tab)

driver.uc_open_with_disconnect(url) # Open in new tab + disconnect()
driver.uc_open_with_disconnect(url, timeout=None) # New tab + sleep()

driver.reconnect(timeout) # disconnect() + sleep(timeout) + connect()

Expand All @@ -1056,7 +1056,15 @@ driver.connect() # Starts the webdriver service to allow actions again

driver.uc_click(selector) # A stealthy click for evading bot-detection

driver.uc_switch_to_frame(frame) # switch_to_frame() in a stealthy way
driver.uc_gui_press_key(key) # Use PyAutoGUI to press the keyboard key

driver.uc_gui_press_keys(keys) # Use PyAutoGUI to press a list of keys

driver.uc_gui_write(text) # Similar to uc_gui_press_keys(), but faster

driver.uc_gui_handle_cf(frame="iframe") # PyAutoGUI click CF Turnstile

driver.uc_switch_to_frame(frame="iframe") # Stealthy switch_to_frame()
```

--------
Expand Down
12 changes: 11 additions & 1 deletion help_docs/uc_mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ driver.uc_open_with_tab(url)

driver.uc_open_with_reconnect(url, reconnect_time=None)

driver.uc_open_with_disconnect(url)
driver.uc_open_with_disconnect(url, timeout=None)

driver.reconnect(timeout)

Expand All @@ -171,6 +171,14 @@ driver.uc_click(
selector, by="css selector",
timeout=settings.SMALL_TIMEOUT, reconnect_time=None)

driver.uc_gui_press_key(key)

driver.uc_gui_press_keys(keys)

driver.uc_gui_write(text)

driver.uc_gui_handle_cf(frame="iframe")

driver.uc_switch_to_frame(frame, reconnect_time=None)
```

Expand Down Expand Up @@ -211,6 +219,8 @@ driver.reconnect("breakpoint")
<li>Timing. (<b translate="no">UC Mode</b> methods let you customize default values that aren't good enough for your environment.)</li>
<li>Not using <b><code translate="no">driver.uc_click(selector)</code></b> when you need to remain undetected while clicking something.</li>

👤 On Linux, you may need to use `driver.uc_gui_handle_cf()` to successfully bypass a Cloudflare CAPTCHA. If there's more than one iframe on that website (and Cloudflare isn't the first one) then put the CSS Selector of that iframe as the first arg to `driver.uc_gui_handle_cf()`. 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.

👤 To find out if <b translate="no">UC Mode</b> will work at all on a specific site (before adjusting for timing), load your site with the following script:

```python
Expand Down
6 changes: 3 additions & 3 deletions mkdocs_build/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

regex>=2024.5.15
pymdown-extensions>=10.8.1
pipdeptree>=2.22.0
pipdeptree>=2.23.0
python-dateutil>=2.8.2
Markdown==3.6
markdown2==2.4.13
Expand All @@ -12,15 +12,15 @@ Jinja2==3.1.4
click==8.1.7
ghp-import==2.1.0
watchdog==4.0.1
cairocffi==1.7.0
cairocffi==1.7.1
pathspec==0.12.1
Babel==2.15.0
paginate==0.5.6
lxml==5.2.2
pyquery==2.0.0
readtime==3.0.0
mkdocs==1.6.0
mkdocs-material==9.5.26
mkdocs-material==9.5.27
mkdocs-exclude-search==0.6.6
mkdocs-simple-hooks==0.1.5
mkdocs-material-extensions==1.3.1
22 changes: 13 additions & 9 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
pip>=24.0
packaging>=24.0
pip>=24.0;python_version<"3.8"
pip>=24.1;python_version>="3.8"
packaging>=24.0;python_version<"3.8"
packaging>=24.1;python_version>="3.8"
setuptools>=68.0.0;python_version<"3.8"
setuptools>=70.0.0;python_version>="3.8"
setuptools>=70.1.0;python_version>="3.8"
wheel>=0.42.0;python_version<"3.8"
wheel>=0.43.0;python_version>="3.8"
attrs>=23.2.0
certifi>=2024.6.2
exceptiongroup>=1.2.1
filelock>=3.12.2;python_version<"3.8"
filelock>=3.14.0;python_version>="3.8"
filelock>=3.15.4;python_version>="3.8"
platformdirs>=4.0.0;python_version<"3.8"
platformdirs>=4.2.2;python_version>="3.8"
typing-extensions>=4.12.2;python_version>="3.8"
parse>=1.20.1
parse>=1.20.2
parse-type>=0.6.2
pyyaml>=6.0.1
six==1.16.0
Expand All @@ -30,8 +32,9 @@ trio==0.22.2;python_version<"3.8"
trio==0.25.1;python_version>="3.8"
trio-websocket==0.11.1
wsproto==1.2.0
websocket-client==1.8.0;python_version>="3.8"
selenium==4.11.2;python_version<"3.8"
selenium==4.21.0;python_version>="3.8"
selenium==4.22.0;python_version>="3.8"
cssselect==1.2.0
sortedcontainers==2.4.0
fasteners==0.19
Expand Down Expand Up @@ -64,6 +67,7 @@ tabcompleter==1.3.0
pdbp==1.5.0
colorama==0.4.6
pyotp==2.9.0
python-xlib==0.33;platform_system=="Linux"
markdown-it-py==2.2.0;python_version<"3.8"
markdown-it-py==3.0.0;python_version>="3.8"
mdurl==0.1.2
Expand All @@ -73,13 +77,13 @@ rich==13.7.1
# ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.)

coverage==7.2.7;python_version<"3.8"
coverage>=7.5.3;python_version>="3.8"
coverage>=7.5.4;python_version>="3.8"
pytest-cov==4.1.0;python_version<"3.8"
pytest-cov>=5.0.0;python_version>="3.8"
flake8==5.0.4;python_version<"3.9"
flake8==7.0.0;python_version>="3.9"
flake8==7.1.0;python_version>="3.9"
mccabe==0.7.0
pyflakes==2.5.0;python_version<"3.9"
pyflakes==3.2.0;python_version>="3.9"
pycodestyle==2.9.1;python_version<"3.9"
pycodestyle==2.11.1;python_version>="3.9"
pycodestyle==2.12.0;python_version>="3.9"
3 changes: 3 additions & 0 deletions seleniumbase/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import collections
import os
import pdb
try:
import pdbp # (Pdb+) --- Python Debugger Plus
Expand Down Expand Up @@ -34,11 +35,13 @@
pdb.DefaultConfig.truncate_long_lines = False
pdb.DefaultConfig.sticky_by_default = True
colored_traceback.add_hook()
os.environ["SE_AVOID_STATS"] = "true" # Disable Selenium Manager stats
if sys.version_info >= (3, 7):
webdriver.TouchActions = None # Lifeline for past selenium-wire versions
if sys.version_info >= (3, 10):
collections.Callable = collections.abc.Callable # Lifeline for nosetests
del collections # Undo "import collections" / Simplify "dir(seleniumbase)"
del os # Undo "import os" / Simplify "dir(seleniumbase)"
del sys # Undo "import sys" / Simplify "dir(seleniumbase)"
del webdriver # Undo "import webdriver" / Simplify "dir(seleniumbase)"

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.27.5"
__version__ = "4.28.0"
19 changes: 11 additions & 8 deletions seleniumbase/behave/behave_sb.py
Original file line number Diff line number Diff line change
Expand Up @@ -862,14 +862,17 @@ def get_configured_sb(context):
and not sb.headless2
and not sb.xvfb
):
print(
'(Linux uses "-D headless" by default. '
'To override, use "-D headed" / "-D gui". '
'For Xvfb mode instead, use "-D xvfb". '
"Or you can hide this info by using"
'"-D headless" / "-D headless2".)'
)
sb.headless = True
if not sb.undetectable:
print(
'(Linux uses "-D headless" by default. '
'To override, use "-D headed" / "-D gui". '
'For Xvfb mode instead, use "-D xvfb". '
"Or you can hide this info by using"
'"-D headless" / "-D headless2" / "-D uc".)'
)
sb.headless = True
else:
sb.xvfb = True
# Recorder Mode can still optimize scripts in --headless2 mode.
if sb.recorder_mode and sb.headless:
sb.headless = False
Expand Down
Loading