2.1.0 - multiple (hot)fixes and added emulate_touch argument

added a keyword argument to the Chrome constructor: emulate_touch, which, when set to True will mimick the presense of a touch(screen/device) for certain tests. 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.
- fixed bug in latest chrome which closes the window when using v2 in contextmanager mode or get_in method.
This commit is contained in:
Leon 2021-02-04 12:05:37 +01:00 committed by GitHub
commit de55331c53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 32 deletions

View File

@ -16,7 +16,7 @@ from setuptools import setup
setup( setup(
name="undetected-chromedriver", name="undetected-chromedriver",
version="2.0.2", version="2.1.0",
packages=["undetected_chromedriver"], packages=["undetected_chromedriver"],
install_requires=["selenium",], install_requires=["selenium",],
url="https://github.com/ultrafunkamsterdam/undetected-chromedriver", url="https://github.com/ultrafunkamsterdam/undetected-chromedriver",

View File

@ -37,7 +37,7 @@ TARGET_VERSION = 0
class Chrome: class Chrome:
def __new__(cls, *args, enable_console_log=False, **kwargs): def __new__(cls, *args, emulate_touch=False, **kwargs):
if not ChromeDriverManager.installed: if not ChromeDriverManager.installed:
ChromeDriverManager(*args, **kwargs).install() ChromeDriverManager(*args, **kwargs).install()
@ -61,28 +61,25 @@ class Chrome:
{ {
"source": """ "source": """
Object.defineProperty(window, 'navigator', { Object.defineProperty(window, 'navigator', {
value: new Proxy(navigator, { value: new Proxy(navigator, {
has: (target, key) => (key === 'webdriver' ? false : key in target), has: (target, key) => (key === 'webdriver' ? false : key in target),
get: (target, key) => get: (target, key) =>
key === 'webdriver' key === 'webdriver'
? undefined ? undefined
: typeof target[key] === 'function' : typeof target[key] === 'function'
? target[key].bind(target) ? target[key].bind(target)
: target[key] : target[key]
}) })
}); });
""" """
+ (
"console.log = console.dir = console.error = function(){};"
if not enable_console_log
else ""
)
}, },
) )
return instance._orig_get(*args, **kwargs) return instance._orig_get(*args, **kwargs)
instance.get = _get_wrapped instance.get = _get_wrapped
instance.get = _get_wrapped
instance.get = _get_wrapped
original_user_agent_string = instance.execute_script( original_user_agent_string = instance.execute_script(
"return navigator.userAgent" "return navigator.userAgent"
@ -91,11 +88,22 @@ class Chrome:
"Network.setUserAgentOverride", "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
})"""
},
)
logger.info(f"starting undetected_chromedriver.Chrome({args}, {kwargs})") logger.info(f"starting undetected_chromedriver.Chrome({args}, {kwargs})")
return instance return instance
class ChromeOptions: class ChromeOptions:
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
if not ChromeDriverManager.installed: if not ChromeDriverManager.installed:
ChromeDriverManager(*args, **kwargs).install() ChromeDriverManager(*args, **kwargs).install()

View File

@ -119,8 +119,9 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
keep_alive=True, keep_alive=True,
debug_addr=None, debug_addr=None,
user_data_dir=None, user_data_dir=None,
factor=0.5, factor=1,
delay=1, delay=2,
emulate_touch=False,
): ):
p = Patcher(target_path=executable_path) p = Patcher(target_path=executable_path)
@ -165,6 +166,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
extra_args = [] extra_args = []
if options.headless: if options.headless:
extra_args.append("--headless") extra_args.append("--headless")
extra_args.append("--window-size=1920,1080")
self.browser_args = [ self.browser_args = [
find_chrome_executable(), find_chrome_executable(),
@ -195,12 +197,60 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
keep_alive=keep_alive, keep_alive=keep_alive,
) )
if options.headless:
orig_get = self.get
def get_wrapped(*args, **kwargs):
if self.execute_script("return navigator.webdriver"):
self.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"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]
})
});
"""
},
)
self.execute_cdp_cmd(
"Network.setUserAgentOverride",
{
"userAgent": self.execute_script(
"return navigator.userAgent"
).replace("Headless", "")
},
)
return orig_get(*args, **kwargs)
self.get = get_wrapped
if emulate_touch:
self.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"source": """
Object.defineProperty(navigator, 'maxTouchPoints', {
get: () => 1
})"""
},
)
def start_session(self, capabilities=None, browser_profile=None): def start_session(self, capabilities=None, browser_profile=None):
if not capabilities: if not capabilities:
capabilities = self.options.to_capabilities() capabilities = self.options.to_capabilities()
super().start_session(capabilities, browser_profile) super().start_session(capabilities, browser_profile)
def get_in(self, url: str, delay=2.5, factor=1): def get_in(self, url: str, delay=2, factor=1):
""" """
:param url: str :param url: str
:param delay: int :param delay: int
@ -233,10 +283,11 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
try: try:
self.get(url) self.get(url)
finally: finally:
self.close() self.service.stop()
# threading.Timer(factor or self.factor, self.close).start() # threading.Timer(factor or self.factor, self.close).start()
time.sleep(delay or self.delay) time.sleep(delay or self.delay)
self.start_session() self.service.start()
# self.start_session()
def quit(self): def quit(self):
try: try:
@ -263,9 +314,10 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
return self return self
def __exit__(self, exc_type, exc_val, exc_tb): def __exit__(self, exc_type, exc_val, exc_tb):
self.close() self.service.stop()
threading.Timer(self.factor, self.start_session).start() #threading.Timer(self.factor, self.service.start).start()
time.sleep(self.delay) time.sleep(self.delay)
self.service.start()
def __hash__(self): def __hash__(self):
return hash(self.options.debugger_address) return hash(self.options.debugger_address)
@ -274,10 +326,9 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
class Patcher(object): class Patcher(object):
url_repo = "https://chromedriver.storage.googleapis.com" url_repo = "https://chromedriver.storage.googleapis.com"
def __init__(self, target_path=None, force=False, version_main: int = 0): def __init__(
self, target_path="./chromedriver", force=False, version_main: int = 0
if not target_path: ):
target_path = os.path.join(tempfile.gettempdir(), 'undetected_chromedriver', 'chromedriver')
if not IS_POSIX: if not IS_POSIX:
if not target_path[-4:] == ".exe": if not target_path[-4:] == ".exe":
target_path += ".exe" target_path += ".exe"
@ -446,8 +497,8 @@ class Patcher(object):
linect += 1 linect += 1
return linect return linect
def __del__(self):
shutil.rmtree(os.path.dirname(self.target_path), ignore_errors=True)
class ChromeOptions(selenium.webdriver.chrome.webdriver.Options): class ChromeOptions(selenium.webdriver.chrome.webdriver.Options):