From 1a373e7b29bb51219a78c80319352c8f226dc6e2 Mon Sep 17 00:00:00 2001 From: Tin Lai Date: Mon, 16 Sep 2024 23:23:54 +1000 Subject: [PATCH] support stealth driver Signed-off-by: Tin Lai --- echo360/downloader.py | 38 +++++++++++++++++++++++++++++++++- echo360/main.py | 47 +++++++++++++++++++++++++++---------------- requirements.txt | 1 + 3 files changed, 68 insertions(+), 18 deletions(-) diff --git a/echo360/downloader.py b/echo360/downloader.py index fab57bd..0a17fd2 100644 --- a/echo360/downloader.py +++ b/echo360/downloader.py @@ -8,7 +8,7 @@ from .echo_exceptions import EchoLoginError from .utils import naive_versiontuple, PERSISTENT_SESSION_FOLDER - +import pip_ensure_version from pick import pick import selenium from selenium import webdriver @@ -21,6 +21,40 @@ _LOGGER = logging.getLogger(__name__) +def build_stealth_driver( + use_local_binary, + selenium_version_ge_4100, + setup_credential, + user_agent, + log_path, + persistent_session, +): + pip_ensure_version.require_package("undetected-chromedriver") + import undetected_chromedriver as uc + + kwargs = dict() + if persistent_session: + kwargs["user_data_dir"] = PERSISTENT_SESSION_FOLDER + print( + ">> Warning: persistent session *might* not supported by stealth. If it has issue, try to delete '{}' folder.".format( + PERSISTENT_SESSION_FOLDER + ) + ) + + from selenium.webdriver.chrome.options import Options + + opts = Options() + opts.add_argument("--window-size=1920x1080") + opts.add_argument("user-agent={}".format(user_agent)) + + if not selenium_version_ge_4100: + print(">> This version of selenium might not be supported by stealth") + + driver = uc.Chrome(**kwargs) + + return driver + + def build_chrome_driver( use_local_binary, selenium_version_ge_4100, @@ -193,6 +227,8 @@ def __init__( driver_builder = build_chrome_driver elif webdriver_to_use == "firefox": driver_builder = build_firefox_driver + elif webdriver_to_use == "stealth": + driver_builder = build_stealth_driver else: driver_builder = build_phantomjs_driver diff --git a/echo360/main.py b/echo360/main.py index 8e92b63..95bd9a4 100644 --- a/echo360/main.py +++ b/echo360/main.py @@ -114,6 +114,13 @@ def handle_args(): help="Force the echo360.py script to download a local \ binary file for phantomjs (will override system bin)", ) + parser.add_argument( + "--stealth", + action="store_true", + default=False, + dest="use_stealth", + help="Use Stealth Chrome Driver to bypass some bot detection (e.g. useful for FIDO).", + ) parser.add_argument( "--chrome", action="store_true", @@ -222,7 +229,9 @@ def handle_args(): _LOGGER.debug("Hostname: %s, UUID: %s", course_hostname, course_url) webdriver_to_use = "phantomjs" - if args["use_chrome"]: + if args["use_stealth"]: + webdriver_to_use = "stealth" + elif args["use_chrome"]: webdriver_to_use = "chrome" elif args["use_firefox"]: webdriver_to_use = "firefox" @@ -305,28 +314,32 @@ def cmd_exists(x): ) binary_type = "geckodriver" + elif webdriver_to_use == "stealth": + binary_type = None else: from .binary_downloader.phantomjs import ( PhantomjsDownloader as binary_downloader, ) binary_type = "phantomjs" - binary_downloader = binary_downloader() # initialise class - _LOGGER.debug( - "binary_downloader link: %s, bin path: %s", - binary_downloader.get_download_link(), - binary_downloader.get_bin(), - ) - # First test for existance of localbinary file - if not os.path.isfile(binary_downloader.get_bin()): - # If failed, then test for existance of global executable in PATH - if cmd_exists(binary_type): - use_local_binary = False - _LOGGER.debug("Using global binary file") - else: - # None exists, download binary file - start_download_binary(binary_downloader, binary_type) - _LOGGER.debug("Downloading binary file") + + if binary_type: + binary_downloader = binary_downloader() # initialise class + _LOGGER.debug( + "binary_downloader link: %s, bin path: %s", + binary_downloader.get_download_link(), + binary_downloader.get_bin(), + ) + # First test for existance of localbinary file + if not os.path.isfile(binary_downloader.get_bin()): + # If failed, then test for existance of global executable in PATH + if cmd_exists(binary_type): + use_local_binary = False + _LOGGER.debug("Using global binary file") + else: + # None exists, download binary file + start_download_binary(binary_downloader, binary_type) + _LOGGER.debug("Downloading binary file") if download_binary: start_download_binary(binary_downloader, binary_type, manual=True) diff --git a/requirements.txt b/requirements.txt index 2038503..9c2b530 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,5 @@ requests>=2.8.14 gevent>=1.2.2 wget>=3.2 pick==0.6.7 +pip_ensure_version==1.0.0 tqdm