Skip to content

Commit 1949a14

Browse files
committed
Improve prevention of automation-detection
1 parent 33661bf commit 1949a14

File tree

4 files changed

+141
-65
lines changed

4 files changed

+141
-65
lines changed

seleniumbase/core/browser_launcher.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,8 @@ def get_driver(
986986
if is_using_uc(undetectable, browser_name) and mobile_emulator:
987987
mobile_emulator = False
988988
user_agent = None
989+
if page_load_strategy and page_load_strategy.lower() == "none":
990+
settings.PAGE_LOAD_STRATEGY = "none"
989991
proxy_auth = False
990992
proxy_user = None
991993
proxy_pass = None

seleniumbase/fixtures/base_case.py

Lines changed: 105 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ def __initialize_variables(self):
112112
self.__frame_switch_layer = 0 # Used by Recorder-Mode
113113
self.__frame_switch_multi = False # Used by Recorder-Mode
114114
self.__last_saved_url = None # Used by Recorder-Mode
115+
self.__uc_frame_layer = 0
115116
self.__called_setup = False
116117
self.__called_teardown = False
117118
self.__start_time_ms = int(time.time() * 1000.0)
@@ -275,14 +276,25 @@ def open(self, url):
275276
pass # Odd issue where the open did happen. Continue.
276277
else:
277278
raise
278-
if self.driver.current_url == pre_action_url and pre_action_url != url:
279+
if (
280+
self.undetectable
281+
or (
282+
self.driver.current_url == pre_action_url
283+
and pre_action_url != url
284+
)
285+
):
279286
time.sleep(0.1) # Make sure load happens
280287
if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:
281-
self.wait_for_ready_state_complete()
288+
if not self.undetectable:
289+
self.wait_for_ready_state_complete()
290+
else:
291+
time.sleep(0.15)
282292
if self.__needs_minimum_wait():
283293
time.sleep(0.03) # Force a minimum wait, even if skipping waits.
284294
if self.undetectable:
285295
time.sleep(0.02)
296+
if self.undetectable:
297+
self.__uc_frame_layer = 0
286298
if self.demo_mode:
287299
if not js_utils.is_jquery_activated(self.driver):
288300
js_utils.add_js_link(self.driver, constants.JQuery.MIN_JS)
@@ -383,7 +395,10 @@ def click(
383395
except Exception:
384396
pass
385397
# Normal click
386-
element.click()
398+
if not self.undetectable or self.__uc_frame_layer > 0:
399+
element.click()
400+
else:
401+
element.uc_click()
387402
except StaleElementReferenceException:
388403
self.wait_for_ready_state_complete()
389404
time.sleep(0.16)
@@ -401,7 +416,10 @@ def click(
401416
if self.browser == "safari" and by == By.LINK_TEXT:
402417
self.__jquery_click(selector, by=by)
403418
else:
404-
element.click()
419+
if not self.undetectable or self.__uc_frame_layer > 0:
420+
element.click()
421+
else:
422+
element.uc_click()
405423
except ENI_Exception:
406424
self.wait_for_ready_state_complete()
407425
time.sleep(0.1)
@@ -446,7 +464,10 @@ def click(
446464
else:
447465
self.__js_click(selector, by=by)
448466
else:
449-
element.click()
467+
if not self.undetectable or self.__uc_frame_layer > 0:
468+
element.click()
469+
else:
470+
element.uc_click()
450471
except MoveTargetOutOfBoundsException:
451472
self.wait_for_ready_state_complete()
452473
try:
@@ -463,7 +484,10 @@ def click(
463484
timeout=timeout,
464485
original_selector=original_selector,
465486
)
466-
element.click()
487+
if not self.undetectable or self.__uc_frame_layer > 0:
488+
element.click()
489+
else:
490+
element.uc_click()
467491
except WebDriverException as e:
468492
if (
469493
"cannot determine loading status" in e.msg
@@ -486,7 +510,10 @@ def click(
486510
timeout=timeout,
487511
original_selector=original_selector,
488512
)
489-
element.click()
513+
if not self.undetectable or self.__uc_frame_layer > 0:
514+
element.click()
515+
else:
516+
element.uc_click()
490517
latest_window_count = len(self.driver.window_handles)
491518
if (
492519
latest_window_count > pre_window_count
@@ -507,39 +534,39 @@ def click(
507534
# switch to the last one if it exists.
508535
self.switch_to_window(-1)
509536
if settings.WAIT_FOR_RSC_ON_CLICKS:
510-
try:
511-
self.wait_for_ready_state_complete()
512-
except Exception:
513-
pass
514-
if self.__needs_minimum_wait():
515-
time.sleep(0.05)
516-
else:
517-
# A smaller subset of self.wait_for_ready_state_complete()
518-
try:
519-
self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT)
520-
except Exception:
521-
pass
522-
if self.__needs_minimum_wait():
523-
time.sleep(0.026)
524-
if self.undetectable:
525-
time.sleep(0.024)
526-
try:
527-
if self.driver.current_url != pre_action_url:
528-
self.__ad_block_as_needed()
529-
self.__disable_beforeunload_as_needed()
530-
if self.__needs_minimum_wait():
531-
time.sleep(0.026)
532-
if self.undetectable:
533-
time.sleep(0.024)
534-
except Exception:
537+
if not self.undetectable:
535538
try:
536539
self.wait_for_ready_state_complete()
537540
except Exception:
538541
pass
542+
if self.__needs_minimum_wait():
543+
time.sleep(0.05)
544+
else:
545+
time.sleep(0.08)
546+
else:
547+
if not self.undetectable:
548+
# A smaller subset of self.wait_for_ready_state_complete()
549+
try:
550+
self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT)
551+
except Exception:
552+
pass
539553
if self.__needs_minimum_wait():
540554
time.sleep(0.026)
541-
if self.undetectable:
542-
time.sleep(0.024)
555+
try:
556+
if self.driver.current_url != pre_action_url:
557+
self.__ad_block_as_needed()
558+
self.__disable_beforeunload_as_needed()
559+
if self.__needs_minimum_wait():
560+
time.sleep(0.026)
561+
except Exception:
562+
try:
563+
self.wait_for_ready_state_complete()
564+
except Exception:
565+
pass
566+
if self.__needs_minimum_wait():
567+
time.sleep(0.026)
568+
else:
569+
time.sleep(0.08)
543570
if self.demo_mode:
544571
if self.driver.current_url != pre_action_url:
545572
if not js_utils.is_jquery_activated(self.driver):
@@ -1384,7 +1411,10 @@ def click_link_text(self, link_text, timeout=None):
13841411
element = self.wait_for_link_text_visible(link_text, timeout=0.2)
13851412
self.__demo_mode_highlight_if_active(link_text, by="link text")
13861413
try:
1387-
element.click()
1414+
if not self.undetectable or self.__uc_frame_layer > 0:
1415+
element.click()
1416+
else:
1417+
element.uc_click()
13881418
except (
13891419
StaleElementReferenceException,
13901420
ENI_Exception,
@@ -1395,7 +1425,10 @@ def click_link_text(self, link_text, timeout=None):
13951425
element = self.wait_for_link_text_visible(
13961426
link_text, timeout=timeout
13971427
)
1398-
element.click()
1428+
if not self.undetectable or self.__uc_frame_layer > 0:
1429+
element.click()
1430+
else:
1431+
element.uc_click()
13991432
except Exception:
14001433
found_css = False
14011434
text_id = self.get_link_attribute(link_text, "id", False)
@@ -1432,7 +1465,10 @@ def click_link_text(self, link_text, timeout=None):
14321465
element = self.wait_for_link_text_visible(
14331466
link_text, timeout=settings.MINI_TIMEOUT
14341467
)
1435-
element.click()
1468+
if not self.undetectable or self.__uc_frame_layer > 0:
1469+
element.click()
1470+
else:
1471+
element.uc_click()
14361472
latest_window_count = len(self.driver.window_handles)
14371473
if (
14381474
latest_window_count > pre_window_count
@@ -1479,7 +1515,10 @@ def click_partial_link_text(self, partial_link_text, timeout=None):
14791515
if self.browser == "phantomjs":
14801516
if self.is_partial_link_text_visible(partial_link_text):
14811517
element = self.wait_for_partial_link_text(partial_link_text)
1482-
element.click()
1518+
if not self.undetectable or self.__uc_frame_layer > 0:
1519+
element.click()
1520+
else:
1521+
element.uc_click()
14831522
return
14841523
soup = self.get_beautiful_soup()
14851524
html_links = soup.fetch("a")
@@ -1519,7 +1558,10 @@ def click_partial_link_text(self, partial_link_text, timeout=None):
15191558
partial_link_text, by="link text"
15201559
)
15211560
try:
1522-
element.click()
1561+
if not self.undetectable or self.__uc_frame_layer > 0:
1562+
element.click()
1563+
else:
1564+
element.uc_click()
15231565
except (
15241566
StaleElementReferenceException,
15251567
ENI_Exception,
@@ -1530,7 +1572,10 @@ def click_partial_link_text(self, partial_link_text, timeout=None):
15301572
element = self.wait_for_partial_link_text(
15311573
partial_link_text, timeout=timeout
15321574
)
1533-
element.click()
1575+
if not self.undetectable or self.__uc_frame_layer > 0:
1576+
element.click()
1577+
else:
1578+
element.uc_click()
15341579
except Exception:
15351580
found_css = False
15361581
text_id = self.get_partial_link_text_attribute(
@@ -1575,7 +1620,10 @@ def click_partial_link_text(self, partial_link_text, timeout=None):
15751620
element = self.wait_for_partial_link_text(
15761621
partial_link_text, timeout=settings.MINI_TIMEOUT
15771622
)
1578-
element.click()
1623+
if not self.undetectable or self.__uc_frame_layer > 0:
1624+
element.click()
1625+
else:
1626+
element.uc_click()
15791627
latest_window_count = len(self.driver.window_handles)
15801628
if (
15811629
latest_window_count > pre_window_count
@@ -2317,6 +2365,8 @@ def switch_to_frame_of_element(self, selector, by="css selector"):
23172365
time.sleep(0.02)
23182366
try:
23192367
self.switch_to_frame(selector, timeout=1)
2368+
if self.undetectable:
2369+
self.__uc_frame_layer += 1
23202370
return selector
23212371
except Exception:
23222372
if self.is_element_present(selector, by=by):
@@ -3052,6 +3102,8 @@ def switch_to_frame(self, frame, timeout=None):
30523102
else:
30533103
if self.__needs_minimum_wait():
30543104
time.sleep(0.04)
3105+
if self.undetectable:
3106+
self.__uc_frame_layer += 1
30553107
if self.recorder_mode and self._rec_overrides_switch:
30563108
url = self.get_current_url()
30573109
if url and len(url) > 0:
@@ -3099,6 +3151,8 @@ def switch_to_default_content(self):
30993151
self.__extra_actions.append(action)
31003152
return
31013153
self.driver.switch_to.default_content()
3154+
if self.undetectable:
3155+
self.__uc_frame_layer = 0
31023156

31033157
def switch_to_parent_frame(self):
31043158
"""Brings driver control outside the current iframe.
@@ -3121,6 +3175,10 @@ def switch_to_parent_frame(self):
31213175
self.__extra_actions.append(action)
31223176
return
31233177
self.driver.switch_to.parent_frame()
3178+
if self.undetectable:
3179+
self.__uc_frame_layer -= 1
3180+
if self.__uc_frame_layer < 0:
3181+
self.__uc_frame_layer = 0
31243182

31253183
@contextmanager
31263184
def frame_switch(self, frame, timeout=None):
@@ -3135,7 +3193,13 @@ def frame_switch(self, frame, timeout=None):
31353193
if self.__frame_switch_layer >= 2:
31363194
self.__frame_switch_multi = True
31373195
self.switch_to_frame(frame, timeout=timeout)
3196+
if self.undetectable:
3197+
self.__uc_frame_layer += 1
31383198
yield
3199+
if self.undetectable:
3200+
self.__uc_frame_layer -= 1
3201+
if self.__uc_frame_layer < 0:
3202+
self.__uc_frame_layer = 0
31393203
self.switch_to_parent_frame()
31403204
if self.recorder_mode:
31413205
self.__frame_switch_layer -= 1

seleniumbase/undetected/__init__.py

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from .patcher import IS_POSIX
1414
from .patcher import Patcher
1515
from .reactor import Reactor
16+
from .webelement import WebElement
1617

1718
__all__ = (
1819
"Chrome",
@@ -298,6 +299,7 @@ def __init__(
298299
self.reactor = reactor
299300
if options.headless:
300301
self._configure_headless()
302+
self._web_element_cls = WebElement
301303

302304
def __getattribute__(self, item):
303305
if not super().__getattribute__("debug"):
@@ -375,40 +377,32 @@ def _get_cdc_props(self):
375377
let objectToInspect = window,
376378
result = [];
377379
while(objectToInspect !== null)
378-
{
379-
result = result.concat(
380-
Object.getOwnPropertyNames(objectToInspect)
381-
);
382-
objectToInspect = Object.getPrototypeOf(objectToInspect);
383-
}
384-
return result.filter(
385-
i => i.match(/.+_.+_(Array|Promise|Symbol)/ig)
386-
)
380+
{ result = result.concat(
381+
Object.getOwnPropertyNames(objectToInspect)
382+
);
383+
objectToInspect = Object.getPrototypeOf(objectToInspect); }
384+
return result.filter(i => i.match(/^[a-z]{3}_[a-z]{22}_.*/i))
387385
"""
388386
)
389387

390-
def _hook_remove_cdc_props(self):
388+
def _hook_remove_cdc_props(self, cdc_props):
389+
cdc_props_js_array = '[' + str().join(
390+
'"' + p + '", ' for p in cdc_props
391+
)[:-2] + ']'
391392
self.execute_cdp_cmd(
392393
"Page.addScriptToEvaluateOnNewDocument",
393394
{
394-
"source": """
395-
let objectToInspect = window, result = [];
396-
while(objectToInspect !== null)
397-
{
398-
result = result.concat(
399-
Object.getOwnPropertyNames(objectToInspect)
400-
);
401-
objectToInspect = Object.getPrototypeOf(objectToInspect);
402-
}
403-
result.forEach(p => p.match(/.+_.+_(Array|Promise|Symbol)/ig)
404-
&&delete window[p]&&console.log('removed',p))
405-
"""
395+
"source": cdc_props_js_array + (
396+
".forEach(p => "
397+
"delete window[p] && console.log('removed',p));"
398+
)
406399
},
407400
)
408401

409402
def get(self, url):
410-
if self._get_cdc_props():
411-
self._hook_remove_cdc_props()
403+
cdc_props = self._get_cdc_props()
404+
if len(cdc_props) > 0:
405+
self._hook_remove_cdc_props(cdc_props)
412406
return super().get(url)
413407

414408
def add_cdp_listener(self, event_name, callback):

seleniumbase/undetected/webelement.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import selenium.webdriver.remote.webelement
2+
from seleniumbase.config import settings
3+
4+
5+
class WebElement(selenium.webdriver.remote.webelement.WebElement):
6+
def uc_click(self):
7+
super().click()
8+
if hasattr(settings, "SKIP_JS_WAITS") and settings.SKIP_JS_WAITS:
9+
pass
10+
elif (
11+
hasattr(settings, "PAGE_LOAD_STRATEGY")
12+
and settings.PAGE_LOAD_STRATEGY == "none"
13+
):
14+
pass
15+
else:
16+
self._parent.reconnect(0.1)

0 commit comments

Comments
 (0)