Skip to content

Commit

Permalink
3.0.3 #255
Browse files Browse the repository at this point in the history
read #255
  • Loading branch information
ultrafunkamsterdam authored Jul 29, 2021
2 parents add42e2 + 9728826 commit 9ad1bb3
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 59 deletions.
2 changes: 1 addition & 1 deletion undetected_chromedriver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

ChromeOptionsV2 = v2.ChromeOptions
logger = logging.getLogger(__name__)
__version__ = "3.0.2"
__version__ = "3.0.3"


TARGET_VERSION = 0
Expand Down
2 changes: 1 addition & 1 deletion undetected_chromedriver/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
class ChromeOptions(_ChromeOptions):
KEY = "goog:chromeOptions"

session = None
_session = None
emulate_touch = True
mock_permissions = True
mock_chrome_global = False
Expand Down
31 changes: 24 additions & 7 deletions undetected_chromedriver/patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,27 +56,44 @@ def __init__(self, executable_path=None, force=False, version_main: int = 0):
"""

self.force = force
self.executable_path = None

if not executable_path:
executable_path = os.path.join(self.data_path, self.exe_name)
self.executable_path = os.path.join(self.data_path, self.exe_name)

if not IS_POSIX:
if not executable_path[-4:] == ".exe":
executable_path += ".exe"
if executable_path:
if not executable_path[-4:] == ".exe":
executable_path += ".exe"

self.zip_path = os.path.join(self.data_path, self.zip_name)

self.executable_path = os.path.abspath(os.path.join(".", executable_path))
if not executable_path:
self.executable_path = os.path.abspath(
os.path.join(".", self.executable_path)
)

self._custom_exe_path = False

if executable_path:
self._custom_exe_path = True
self.executable_path = executable_path
self.version_main = version_main
self.version_full = None


def auto(self, executable_path=None, force=False, version_main=None):
"""
"""
""""""
if executable_path:
self.executable_path = executable_path
self._custom_exe_path = True

if self._custom_exe_path:
ispatched = self.is_binary_patched(self.executable_path)
if not ispatched:
return self.patch_exe()
else:
return

if version_main:
self.version_main = version_main
if force is True:
Expand Down
111 changes: 61 additions & 50 deletions undetected_chromedriver/v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,25 @@
import selenium.webdriver.common.service
import selenium.webdriver.remote.webdriver

from .cdp import CDP
from .options import ChromeOptions
from .patcher import IS_POSIX, Patcher
from .reactor import Reactor
from .cdp import CDP

__all__ = ("Chrome", "ChromeOptions", "Patcher", "Reactor", "CDP", "find_chrome_executable")
__all__ = (
"Chrome",
"ChromeOptions",
"Patcher",
"Reactor",
"CDP",
"find_chrome_executable",
)

logger = logging.getLogger("uc")
logger.setLevel(logging.getLogger().getEffectiveLevel())


class Chrome(selenium.webdriver.Chrome):
class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
"""
Controls the ChromeDriver and allows you to drive the browser.
Expand Down Expand Up @@ -67,6 +74,7 @@ class Chrome(selenium.webdriver.Chrome):
"""

_instances = set()
session_id = None

def __init__(
self,
Expand Down Expand Up @@ -129,17 +137,14 @@ def __init__(
Specify whether you want to use the browser in headless mode.
warning: this lowers undetectability and not fully supported.
emulate_touch: bool, optional, default: False
if set to True, patches window.maxTouchPoints to always return non-zero
delay: int, optional, default: 5
delay in seconds to wait before giving back control.
this is used only when using the context manager
(`with` statement) to bypass, for example CloudFlare.
5 seconds is a foolproof value.
version_main: int, optional, default: None (=auto)
if you, for god knows whatever reason, use
if you, for god knows whatever reason, use
an older version of Chrome. You can specify it's full rounded version number
here. Example: 87 for all versions of 87
Expand All @@ -149,22 +154,28 @@ def __init__(
setting it is not recommended, unless you know the implications and think
you might need it.
"""
patcher = Patcher(executable_path=executable_path, force=patcher_force_close, version_main=version_main)

patcher = Patcher(
executable_path=executable_path,
force=patcher_force_close,
version_main=version_main,
)
patcher.auto()

if not options:
options = ChromeOptions()

try:
if options.session and options.session is not None:
if hasattr(options, "_session") and options._session is not None:

# prevent reuse of options,
# as it just appends arguments, not replace them
# you'll get conflicts starting chrome
raise RuntimeError("you cannot reuse the ChromeOptions object")
except AttributeError:
pass

options.session = self
options._session = self

debug_port = selenium.webdriver.common.service.utils.free_port()
debug_host = "127.0.0.1"
Expand Down Expand Up @@ -250,9 +261,9 @@ def __init__(
options.add_argument("--window-size=1920,1080")
options.add_argument("--start-maximized")
options.add_argument("--no-sandbox")
# fixes "could not connect to chrome" error when running
# fixes "could not connect to chrome" error when running
# on linux using privileged user like root (which i don't recommend)

options.add_argument(
"--log-level=%d" % log_level
or divmod(logging.getLogger().getEffectiveLevel(), 10)[0]
Expand Down Expand Up @@ -280,15 +291,16 @@ def __init__(
if not desired_capabilities:
desired_capabilities = options.to_capabilities()


self.browser = subprocess.Popen(
[options.binary_location, *options.arguments],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True,
close_fds=IS_POSIX,
)

super().__init__(
super(Chrome, self).__init__(
executable_path=patcher.executable_path,
port=port,
options=options,
Expand Down Expand Up @@ -320,10 +332,22 @@ def __init__(
reactor.start()
self.reactor = reactor


if options.headless:
self._configure_headless()

orig_get = self.get

# def get_wrapped(*args, **kwargs):

# self.execute_cdp_cmd(
# "Network.setExtraHTTPHeaders",
# {"headers": {"dnt": "1", "cache-control": "no-cache"}},
# )
#
# return orig_get(*args, **kwargs)
#
# self.get = get_wrapped

def _configure_headless(self):

orig_get = self.get
Expand Down Expand Up @@ -508,12 +532,12 @@ def add_cdp_listener(self, event_name, callback):
self.reactor.add_event_handler(event_name, callback)
return self.reactor.handlers
return False

def clear_cdp_listeners(self):
if self.reactor and isinstance(self.reactor, Reactor):
self.reactor.handlers.clear()

def tab_new(self, url:str):
def tab_new(self, url: str):
"""
this opens a url in a new tab.
apparently, that passes all tests directly!
Expand All @@ -526,17 +550,18 @@ def tab_new(self, url:str):
-------
"""
if not hasattr(self, 'cdp'):
if not hasattr(self, "cdp"):
from .cdp import CDP

self.cdp = CDP(self.options)
self.cdp.tab_new(url)

def reconnect(self):
def reconnect(self, timeout=0.1):
try:
self.service.stop()
except Exception as e:
logger.debug(e)

time.sleep(timeout)
try:
self.service.start()
except Exception as e:
Expand All @@ -550,31 +575,33 @@ def reconnect(self):
def start_session(self, capabilities=None, browser_profile=None):
if not capabilities:
capabilities = self.options.to_capabilities()
super().start_session(capabilities, browser_profile)
super(Chrome, self).start_session(capabilities, browser_profile)


def quit(self):
logger.debug("closing webdriver")
self.service.process.kill()
try:
if self.reactor and isinstance(self.reactor, Reactor):
self.reactor.event.set()
super().quit()

except Exception: # noqa
pass
try:
logger.debug("killing browser")
self.browser.kill()
self.browser.terminate()
self.browser.wait(1)

except TimeoutError as e:
logger.debug(e, exc_info=True)
except Exception: # noqa
pass

if hasattr(self, 'keep_user_data_dir') \
and not self.keep_user_data_dir \
or self.keep_user_data_dir is False:
for _ in range(3):
if (
hasattr(self, "keep_user_data_dir")
and hasattr(self, "user_data_dir")
and not self.keep_user_data_dir
):
for _ in range(5):
try:
logger.debug("removing profile : %s" % self.user_data_dir)
shutil.rmtree(self.user_data_dir, ignore_errors=False)
Expand All @@ -585,15 +612,16 @@ def quit(self):
"permission error. files are still in use/locked. retying..."
)
except (RuntimeError, OSError) as e:
logger.debug(
"%s retying..." % e
)
logger.debug("%s retying..." % e)
else:
break
time.sleep(.25)
time.sleep(0.1)

def __del__(self):
logger.debug("Chrome.__del__")
try:
self.service.process.kill()
except:
pass
self.quit()

def __enter__(self):
Expand All @@ -608,23 +636,6 @@ def __exit__(self, exc_type, exc_val, exc_tb):
def __hash__(self):
return hash(self.options.debugger_address)

def find_elements_by_text(self, text: str):
for elem in self.find_elements_by_css_selector("*"):
try:
if text.lower() in elem.text.lower():
yield elem
except Exception as e:
logger.debug("find_elements_by_text: %s" % e)

def find_element_by_text(self, text: str, selector=None):
if not selector:
selector = "*"
for elem in self.find_elements_by_css_selector(selector):
try:
if text.lower() in elem.text.lower():
return elem
except Exception as e:
logger.debug("find_elements_by_text: {}".format(e))


def find_chrome_executable():
Expand Down

0 comments on commit 9ad1bb3

Please # to comment.