- added a keyword argument to the Chrome constructor: `emulate_touch` which, when set to True will report presense of a touch(screen/device). This is mainly for bet365 detections. Otherwise i would not recommend setting it.
credits to @boganfoo for this excellent find! - removed `enable_console_log` - reverted back to storing chromedriver binary in current workdir after several reports of users entire project folder being deleted (sorry for that btw). - some code cleanup - added a fix for useragent in headless mode in v2 (which reported 'Headless'). still headless mode in v2 is under construction and not fully functional.
This commit is contained in:
parent
02ebb18c78
commit
a50362f9e8
|
@ -19,11 +19,11 @@ by UltrafunkAmsterdam (https://github.com/ultrafunkamsterdam)
|
|||
import io
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import string
|
||||
import sys
|
||||
import zipfile
|
||||
import string
|
||||
import random
|
||||
from distutils.version import LooseVersion
|
||||
from urllib.request import urlopen, urlretrieve
|
||||
|
||||
|
@ -32,6 +32,7 @@ from selenium.webdriver import ChromeOptions as _ChromeOptions
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
TARGET_VERSION = 0
|
||||
|
||||
|
||||
|
@ -60,54 +61,50 @@ class Chrome:
|
|||
{
|
||||
"source": """
|
||||
|
||||
Object.defineProperty(window, 'navigator', {
|
||||
value: new Proxy(navigator, {
|
||||
has: (target, key) => (key === 'webdriver' ? false : key in target),
|
||||
get: (target, key) =>
|
||||
key === 'webdriver'
|
||||
? undefined
|
||||
: typeof target[key] === 'function'
|
||||
? target[key].bind(target)
|
||||
: target[key]
|
||||
})
|
||||
});
|
||||
"""
|
||||
Object.defineProperty(window, 'navigator', {
|
||||
value: new Proxy(navigator, {
|
||||
has: (target, key) => (key === 'webdriver' ? false : key in target),
|
||||
get: (target, key) =>
|
||||
key === 'webdriver'
|
||||
? undefined
|
||||
: typeof target[key] === 'function'
|
||||
? target[key].bind(target)
|
||||
: target[key]
|
||||
})
|
||||
});
|
||||
"""
|
||||
},
|
||||
)
|
||||
return instance._orig_get(*args, **kwargs)
|
||||
|
||||
instance.get = _get_wrapped
|
||||
instance.get = _get_wrapped
|
||||
instance.get = _get_wrapped
|
||||
|
||||
original_user_agent_string = instance.execute_script(
|
||||
"return navigator.userAgent"
|
||||
)
|
||||
instance.execute_cdp_cmd(
|
||||
"Network.setUserAgentOverride",
|
||||
{
|
||||
"userAgent": original_user_agent_string.replace("Headless", ""),
|
||||
},
|
||||
{"userAgent": original_user_agent_string.replace("Headless", ""),},
|
||||
)
|
||||
if emulate_touch:
|
||||
instance.execute_cdp_cmd(
|
||||
"Page.addScriptToEvaluateOnNewDocument",
|
||||
{
|
||||
"source": """
|
||||
Object.defineProperty(navigator, 'maxTouchPoints', {
|
||||
get: () => 1
|
||||
})"""
|
||||
Object.defineProperty(navigator, 'maxTouchPoints', {
|
||||
get: () => 1
|
||||
})"""
|
||||
},
|
||||
)
|
||||
|
||||
logger.info(f"starting undetected_chromedriver.Chrome({args}, {kwargs})")
|
||||
return instance
|
||||
|
||||
|
||||
class ChromeOptions:
|
||||
__doc__ = _ChromeOptions.__doc__
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
__doc__ = _ChromeOptions.__new__.__doc__
|
||||
|
||||
if not ChromeDriverManager.installed:
|
||||
ChromeDriverManager(*args, **kwargs).install()
|
||||
if not ChromeDriverManager.selenium_patched:
|
||||
|
@ -122,6 +119,7 @@ class ChromeOptions:
|
|||
|
||||
|
||||
class ChromeDriverManager(object):
|
||||
|
||||
installed = False
|
||||
selenium_patched = False
|
||||
target_version = None
|
||||
|
@ -234,10 +232,10 @@ class ChromeDriverManager(object):
|
|||
@staticmethod
|
||||
def random_cdc():
|
||||
cdc = random.choices(string.ascii_lowercase, k=26)
|
||||
cdc[-6:-4] = map(str.upper, cdc[-6:-4])
|
||||
cdc[-6: -4] = map(str.upper, cdc[-6: -4])
|
||||
cdc[2] = cdc[0]
|
||||
cdc[3] = "_"
|
||||
return "".join(cdc).encode()
|
||||
cdc[3] = '_'
|
||||
return ''.join(cdc).encode()
|
||||
|
||||
def patch_binary(self):
|
||||
"""
|
||||
|
|
|
@ -46,6 +46,8 @@ import tempfile
|
|||
import threading
|
||||
import time
|
||||
import zipfile
|
||||
import atexit
|
||||
import contextlib
|
||||
from distutils.version import LooseVersion
|
||||
from urllib.request import urlopen, urlretrieve
|
||||
|
||||
|
@ -74,10 +76,8 @@ def find_chrome_executable():
|
|||
for item in os.environ.get("PATH").split(os.pathsep):
|
||||
for subitem in ("google-chrome", "chromium", "chromium-browser"):
|
||||
candidates.add(os.sep.join((item, subitem)))
|
||||
if "darwin" in sys.platform:
|
||||
candidates.update(
|
||||
["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"]
|
||||
)
|
||||
if 'darwin' in sys.platform:
|
||||
candidates.update(["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"])
|
||||
else:
|
||||
for item in map(
|
||||
os.environ.get, ("PROGRAMFILES", "PROGRAMFILES(X86)", "LOCALAPPDATA")
|
||||
|
@ -94,18 +94,16 @@ def find_chrome_executable():
|
|||
|
||||
|
||||
class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
||||
__doc__ = (
|
||||
"""\
|
||||
--------------------------------------------------------------------------
|
||||
NOTE:
|
||||
Chrome has everything included to work out of the box.
|
||||
it does not `need` customizations.
|
||||
any customizations MAY lead to trigger bot migitation systems.
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
"""
|
||||
+ selenium.webdriver.remote.webdriver.WebDriver.__doc__
|
||||
)
|
||||
__doc__ = """\
|
||||
--------------------------------------------------------------------------
|
||||
NOTE:
|
||||
Chrome has everything included to work out of the box.
|
||||
it does not `need` customizations.
|
||||
any customizations MAY lead to trigger bot migitation systems.
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
""" + selenium.webdriver.remote.webdriver.WebDriver.__doc__
|
||||
|
||||
_instances = set()
|
||||
|
||||
|
@ -125,13 +123,9 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
delay=1,
|
||||
emulate_touch=False,
|
||||
):
|
||||
self._data_dir_default = True
|
||||
if user_data_dir:
|
||||
self._data_dir_default = False
|
||||
|
||||
p = Patcher(target_path=executable_path)
|
||||
p.auto(False)
|
||||
|
||||
self._patcher = p
|
||||
self.factor = factor
|
||||
self.delay = delay
|
||||
|
@ -172,7 +166,6 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
extra_args = []
|
||||
if options.headless:
|
||||
extra_args.append("--headless")
|
||||
options.add_argument("start-maximized")
|
||||
|
||||
self.browser_args = [
|
||||
find_chrome_executable(),
|
||||
|
@ -245,9 +238,9 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
"Page.addScriptToEvaluateOnNewDocument",
|
||||
{
|
||||
"source": """
|
||||
Object.defineProperty(navigator, 'maxTouchPoints', {
|
||||
get: () => 1
|
||||
})"""
|
||||
Object.defineProperty(navigator, 'maxTouchPoints', {
|
||||
get: () => 1
|
||||
})"""
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -256,10 +249,11 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
capabilities = self.options.to_capabilities()
|
||||
super().start_session(capabilities, browser_profile)
|
||||
|
||||
def get_in(self, url: str, delay=2.5):
|
||||
def get_in(self, url: str, delay=2.5, factor=1):
|
||||
"""
|
||||
:param url: str
|
||||
:param delay: disconnect <delay> seconds after .get()
|
||||
:param delay: int
|
||||
:param factor: disconnect <factor> seconds after .get()
|
||||
too low will disconnect before get() fired.
|
||||
|
||||
=================================================
|
||||
|
@ -289,8 +283,8 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
self.get(url)
|
||||
finally:
|
||||
self.close()
|
||||
threading.Timer(delay, self.close).start()
|
||||
time.sleep(delay)
|
||||
# threading.Timer(factor or self.factor, self.close).start()
|
||||
time.sleep(delay or self.delay)
|
||||
self.start_session()
|
||||
|
||||
def quit(self):
|
||||
|
@ -306,8 +300,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
except Exception: # noqa
|
||||
pass
|
||||
try:
|
||||
if self._data_dir_default:
|
||||
shutil.rmtree(self.user_data_dir, ignore_errors=False)
|
||||
shutil.rmtree(self.user_data_dir, ignore_errors=False)
|
||||
except PermissionError:
|
||||
time.sleep(1)
|
||||
self.quit()
|
||||
|
@ -331,7 +324,7 @@ class Patcher(object):
|
|||
url_repo = "https://chromedriver.storage.googleapis.com"
|
||||
|
||||
def __init__(
|
||||
self, target_path="./chromedriver", force=False, version_main: int = 0
|
||||
self, target_path="./chromedriver", force=False, version_main: int = 0
|
||||
):
|
||||
if not IS_POSIX:
|
||||
if not target_path[-4:] == ".exe":
|
||||
|
@ -378,7 +371,14 @@ class Patcher(object):
|
|||
:return: version string
|
||||
:rtype: LooseVersion
|
||||
"""
|
||||
path = ("/latest_release" if not self.version_main else f"/latest_release_{self.version_main}").upper()
|
||||
path = (
|
||||
"/"
|
||||
+ (
|
||||
"latest_release"
|
||||
if not self.version_main
|
||||
else f"latest_release_{self.version_main}"
|
||||
).upper()
|
||||
)
|
||||
logger.debug("getting release number from %s" % path)
|
||||
return LooseVersion(urlopen(self.url_repo + path).read().decode())
|
||||
|
||||
|
@ -411,7 +411,7 @@ class Patcher(object):
|
|||
os.makedirs(os.path.dirname(self.target_path), mode=0o755)
|
||||
except OSError:
|
||||
pass
|
||||
with zipfile.ZipFile(self.zipname, mode="r") as zf:
|
||||
with zipfile.ZipFile(self.zipname, mode='r') as zf:
|
||||
zf.extract(self.exename)
|
||||
os.rename(self.exename, self.target_path)
|
||||
os.remove(self.zipname)
|
||||
|
@ -480,6 +480,7 @@ class Patcher(object):
|
|||
|
||||
:return: False on failure, binary name on success
|
||||
"""
|
||||
|
||||
logger.info("patching driver executable %s" % self.target_path)
|
||||
|
||||
linect = 0
|
||||
|
@ -493,6 +494,9 @@ class Patcher(object):
|
|||
linect += 1
|
||||
return linect
|
||||
|
||||
def __del__(self):
|
||||
shutil.rmtree(os.path.dirname(self.target_path), ignore_errors=True)
|
||||
|
||||
|
||||
class ChromeOptions(selenium.webdriver.chrome.webdriver.Options):
|
||||
pass
|
||||
|
|
Loading…
Reference in New Issue