diff --git a/undetected_chromedriver/__init__.py b/undetected_chromedriver/__init__.py index fff8159..ac3eeb2 100644 --- a/undetected_chromedriver/__init__.py +++ b/undetected_chromedriver/__init__.py @@ -18,7 +18,7 @@ by UltrafunkAmsterdam (https://github.com/ultrafunkamsterdam) """ -__version__ = "3.1.2" +__version__ = "3.1.5" import json import logging @@ -107,6 +107,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver): enable_cdp_events=False, service_args=None, desired_capabilities=None, + advanced_elements=False, service_log_path=None, keep_alive=True, log_level=0, @@ -153,12 +154,26 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver): driver.add_cdp_listener("Network.dataReceived", yourcallback) # yourcallback is an callable which accepts exactly 1 dict as parameter + service_args: list of str, optional, default: None arguments to pass to the driver service desired_capabilities: dict, optional, default: None - auto from config Dictionary object with non-browser specific capabilities only, such as "item" or "loggingPref". + advanced_webelements: bool, optional, default: False + makes it easier to recognize elements like you know them from html/browser inspection, especially when working + in an interactive environment + + default webelement repr: + + + advanced webelement repr + )> + + note: when retrieving large amounts of elements ( example: find_elements_by_tag("*") ) and print them, it does take a little more time. + + service_log_path: str, optional, default: None path to log information from the driver. @@ -361,7 +376,9 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver): desired_capabilities = options.to_capabilities() if not use_subprocess: - self.browser_pid = start_detached(options.binary_location, *options.arguments) + self.browser_pid = start_detached( + options.binary_location, *options.arguments + ) else: browser = subprocess.Popen( [options.binary_location, *options.arguments], @@ -372,8 +389,6 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver): ) self.browser_pid = browser.pid - - super(Chrome, self).__init__( executable_path=patcher.executable_path, port=port, @@ -395,6 +410,11 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver): reactor.start() self.reactor = reactor + if advanced_elements: + from .webelement import WebElement + + self._web_element_cls = WebElement + if options.headless: self._configure_headless() @@ -534,9 +554,8 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver): """ if not hasattr(self, "cdp"): from .cdp import CDP - - self.cdp = CDP(self.options) - self.cdp.tab_new(url) + cdp = CDP(self.options) + cdp.tab_new(url) def reconnect(self, timeout=0.1): try: @@ -564,7 +583,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver): def quit(self): logger.debug("closing webdriver") - if hasattr(self, 'service') and getattr(self.service, 'process', None): + if hasattr(self, "service") and getattr(self.service, "process", None): self.service.process.kill() try: if self.reactor and isinstance(self.reactor, Reactor): @@ -606,11 +625,10 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver): # this must come last, otherwise it will throw 'in use' errors self.patcher = None - def __del__(self): try: self.service.process.kill() - except: + except: # noqa pass self.quit() @@ -640,7 +658,13 @@ def find_chrome_executable(): candidates = set() if IS_POSIX: for item in os.environ.get("PATH").split(os.pathsep): - for subitem in ("google-chrome", "chromium", "chromium-browser", "chrome"): + for subitem in ( + "google-chrome", + "chromium", + "chromium-browser", + "chrome", + "google-chrome-stable", + ): candidates.add(os.sep.join((item, subitem))) if "darwin" in sys.platform: candidates.update( diff --git a/undetected_chromedriver/patcher.py b/undetected_chromedriver/patcher.py index e36d5e7..4bc13e7 100644 --- a/undetected_chromedriver/patcher.py +++ b/undetected_chromedriver/patcher.py @@ -62,7 +62,6 @@ class Patcher(object): prefix = secrets.token_hex(8) if not executable_path: - self.executable_path = os.path.join( self.data_path, "_".join([prefix, self.exe_name]) ) @@ -72,7 +71,7 @@ class Patcher(object): if not executable_path[-4:] == ".exe": executable_path += ".exe" - # self.zip_path = os.path.join(self.data_path, self.zip_name) + self.zip_path = os.path.join(self.data_path, self.zip_name) if not executable_path: self.executable_path = os.path.abspath( @@ -84,11 +83,6 @@ class Patcher(object): if executable_path: self._custom_exe_path = True self.executable_path = executable_path - self.data_path = os.path.dirname(executable_path) - - self.zip_path = os.path.join( - os.path.dirname(self.executable_path), self.exe_name - ) self.version_main = version_main self.version_full = None @@ -130,7 +124,6 @@ class Patcher(object): self.version_main = release.version[0] self.version_full = release self.unzip_package(self.fetch_package()) - # i.patch() return self.patch() def patch(self): @@ -181,10 +174,12 @@ class Patcher(object): pass os.makedirs(os.path.dirname(self.zip_path), mode=0o755, exist_ok=True) - with zipfile.ZipFile(fp, mode="r") as zf: zf.extract(self.exe_name, os.path.dirname(self.zip_path)) - os.rename(self.zip_path, self.executable_path) + os.rename( + os.path.join(self.data_path, self.exe_name), + self.executable_path + ) os.remove(fp) os.chmod(self.executable_path, 0o755) return self.executable_path diff --git a/undetected_chromedriver/webelement.py b/undetected_chromedriver/webelement.py index d1bcbee..f37709a 100644 --- a/undetected_chromedriver/webelement.py +++ b/undetected_chromedriver/webelement.py @@ -13,6 +13,7 @@ class WebElement(selenium.webdriver.remote.webelement.WebElement): )> """ + _attrs = {} @property @@ -36,5 +37,3 @@ class WebElement(selenium.webdriver.remote.webelement.WebElement): if strattrs: strattrs = " " + strattrs return f"<{self.__class__.__name__}(<{self.tag_name}{strattrs}>)>" - -