From d7a826db4789d4fa9d37fee21dcc85fde930cb70 Mon Sep 17 00:00:00 2001 From: Leon Date: Sun, 22 Dec 2019 13:48:58 +0100 Subject: [PATCH] Add files via upload --- __init__.py | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 __init__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..0535166 --- /dev/null +++ b/__init__.py @@ -0,0 +1,271 @@ +#!/usr/bin/env python3 + + +""" + + 888 888 d8b + 888 888 Y8P + 888 888 + .d8888b 88888b. 888d888 .d88b. 88888b.d88b. .d88b. .d88888 888d888 888 888 888 .d88b. 888d888 +d88P" 888 "88b 888P" d88""88b 888 "888 "88b d8P Y8b d88" 888 888P" 888 888 888 d8P Y8b 888P" +888 888 888 888 888 888 888 888 888 88888888 888 888 888 888 Y88 88P 88888888 888 +Y88b. 888 888 888 Y88..88P 888 888 888 Y8b. Y88b 888 888 888 Y8bd8P Y8b. 888 + "Y8888P 888 888 888 "Y88P" 888 888 888 "Y8888 "Y88888 888 888 Y88P "Y8888 888 88888888 + + 888 d8b 888 888 + 888 Y8P 888 888 + 888 888 888 + 88888b. 888 .d88888 .d88888 .d88b. 88888b. + 888 "88b 888 d88" 888 d88" 888 d8P Y8b 888 "88b + 888 888 888 888 888 888 888 88888888 888 888 + 888 888 888 Y88b 888 Y88b 888 Y8b. 888 888 + 888 888 888 "Y88888 "Y88888 "Y8888 888 888 + +BY ULTRAFUNKAMSTERDAM (https://github.com/ultrafunkamsterdam) + + +################################################################## + +Optimized Selenium Chromedriver patch which does not trigger anti-bot services like Distill Network. +Automatically downloads the driver binary and patches it. + +Not tested on Chrome higher than 79! + +################################################################# + + +USAGE + +# 1- by far the easiest + +from undetected_chromedriver import Chrome, ChromeOptions +driver = Chrome() +driver.get('https://distilnetworks.com') + + + +# 2- patches current selenium instance (for current session) + +import undetected_chromedriver +undetected_chromedriver.install() + +from selenium.webdriver import Chrome +driver = Chrome() +driver.get('https://distilnetworks.com') + + +# 3 - Customized + +import undetected_chromedriver + +#specify chromedriver version to download and patch +undetected_chromedriver.TARGET_VERSION = 78 + +# or specify your own chromedriver binary to patch +undetected_chromedriver.install( + executable_path='c:/users/user1/chromedriver.exe', + target_version=78 +) + +from selenium.webdriver import Chrome, ChromeOptions +opts = ChromeOptions() +opts.add_argument(f'--proxy-server=socks5://127.0.0.1:9050') +driver = Chrome(options=opts) +driver.get('https://distilnetworks.com') + + +a combination of function(s) from this module :) + +""" + +import io +import logging +import os +import sys +import zipfile +from urllib.request import urlopen, urlretrieve + +from selenium.webdriver import Chrome as _Chrome +from selenium.webdriver import ChromeOptions as _ChromeOptions + + +_DL_BASE = "https://chromedriver.storage.googleapis.com/" +TARGET_VERSION = 79 +__is_patched__ = 0 + + +class Chrome: + def __new__(cls, *args, **kwargs): + if not ChromeDriverManager.installed: + ChromeDriverManager(*args, **kwargs).install() + if not ChromeDriverManager.selenium_patched: + ChromeDriverManager(*args, **kwargs).patch_selenium_webdriver() + instance = object.__new__(_Chrome) + instance.__init__(*args, **kwargs) + instance.execute_cdp_cmd( + "Page.addScriptToEvaluateOnNewDocument", + { + "source": """ + Object.defineProperty(navigator, 'webdriver', { + get: () => undefined + }) + """ + }, + ) + original_user_agent_string = instance.execute_script( + "return navigator.userAgent" + ) + instance.execute_cdp_cmd( + "Network.setUserAgentOverride", + { + "userAgent": original_user_agent_string.replace("Headless", ""), + "platform": "Windows", + }, + ) + print(f"starting webdriver instance Chrome({args}, {kwargs})") + return instance + + +class ChromeOptions: + def __new__(cls, *args, **kwargs): + if not ChromeDriverManager.installed: + ChromeDriverManager(*args, **kwargs).install() + if not ChromeDriverManager.selenium_patched: + ChromeDriverManager(*args, **kwargs).patch_selenium_webdriver() + instance = object.__new__(_ChromeOptions) + instance.__init__() + print(f"starting options instance ChromeOptions({args}, {kwargs})") + return instance + + # return _ChromeOptions() + + +class ChromeDriverManager(object): + installed = False + selenium_patched = False + + def __init__(self, executable_path=None, target_version=None, *args, **kwargs): + self.executable_path = executable_path or "chromedriver.exe" + self.platform = sys.platform + self.target_version = target_version + + def patch_selenium_webdriver(self_): + """ + Patches existing webdriver path on OR if executable_path is None, will download + and patch a new webdriver binary for chrome (automatically finds latest release of main version) + + :param str executable_path: OPTIONAL path to existing chromedriver executable to patch + :param int version_int: OPTIONAL target chrome main version. default 79 + :return: + """ + import selenium.webdriver.chrome.service + import selenium.webdriver + + # Monkeypatching ChromeDriver Service + if self_.__class__.selenium_patched: + return + + Service__init__ = selenium.webdriver.chrome.service.Service.__init__ + + def patched_Service__init__(self, *a, **k): + logging.warning("Using patched ChromeDriver Service class") + Service__init__(self, self_.executable_path, **k) + + selenium.webdriver.chrome.service.Service.__init__ = patched_Service__init__ + + # monkeypatching ChromeOptions + ChromeOptions__init__ = selenium.webdriver.ChromeOptions.__init__ + + def patched_ChromeOptions__init__(self): + logging.warning("Using patched ChromeOptions class") + ChromeOptions__init__(self) + self.add_argument("start-maximized") + self.add_experimental_option("excludeSwitches", ["enable-automation"]) + self.add_experimental_option("useAutomationExtension", False) + + selenium.webdriver.ChromeOptions.__init__ = patched_ChromeOptions__init__ + logging.warning( + "Now it is safe to import Chrome and ChromeOptions from selenium" + ) + self_.__class__.selenium_patched = True + + def install(self, patch_selenium=True): + """ + Initialize the patch + + This will: + download chromedriver if not present + patch the downloaded chromedriver + patch selenium package if is True (default) + + :param patch_selenium: patch selenium webdriver classes for Chrome and ChromeDriver (for current python session) + :return: + """ + if ( + not self.__class__.installed + or not __is_patched__ + or not os.path.exists(self.executable_path) + ): + self.fetch_chromedriver() + self.patch_binary() + self.__class__.installed = True + + if patch_selenium: + self.patch_selenium_webdriver() + + def get_release_version_number(self): + """ + Gets the latest major version available, or the latest major version of self.target_version if set explicitly. + + :return: version string + """ + path = ( + "LATEST_RELEASE" + if not self.target_version + else f"LATEST_RELEASE_{self.target_version}" + ) + return urlopen(_DL_BASE + path).read().decode() + + def fetch_chromedriver(self): + """ + Downloads ChromeDriver from source and unpacks the executable + + :return: on success, name of the unpacked executable + """ + base_ = "chromedriver{}" + exe_name = base_.format(".exe") + zip_name = base_.format(".zip") + ver = self.get_release_version_number() + urlretrieve( + f"{_DL_BASE}{ver}/{base_.format(f'_{self.platform}')}.zip", + filename=zip_name, + ) + with zipfile.ZipFile(zip_name) as zf: + zf.extract(exe_name) + os.remove(zip_name) + return exe_name + + def patch_binary(self): + """ + Patches the ChromeDriver binary + + :return: False on failure, binary name on success + """ + if self.__class__.installed: + return + + with io.open(self.executable_path, "r+b") as binary: + for line in iter(lambda: binary.readline(), b""): + if b"cdc_" in line: + binary.seek(-len(line), 1) + line = b" var key = '$azc_abcdefghijklmnopQRstuv_';\n" + binary.write(line) + __is_patched__ = 1 + break + else: + return False + return True + + +def install(executable_path=None, target_version=TARGET_VERSION, *args, **kwargs): + ChromeDriverManager(executable_path, target_version, *args, **kwargs).install()