美文网首页
Selenium源码分析之chromedriver的设置

Selenium源码分析之chromedriver的设置

作者: 假程序员 | 来源:发表于2019-04-05 15:25 被阅读0次

    ide:pycharm;
    language:python;
    selenium:3.141.0

    启动pycharm,并创建名为seleniumHQ的工程,创建一个py文件,输入如下代码:

    if __name__ == '__main__':
        from selenium.webdriver.chrome.webdriver import WebDriver
        path="/Users/apple/Seleniumdriver/chromedriver"
        driver = WebDriver(executable_path=path)
        driver.get("https://www.baidu.com")
        driver.close()
        driver.quit()
    

    进入WebDriver类源码

    class WebDriver(RemoteWebDriver):
        """
        Controls the ChromeDriver and allows you to drive the browser.
    
        You will need to download the ChromeDriver executable from
        http://chromedriver.storage.googleapis.com/index.html
        """
    
        def __init__(self, executable_path="chromedriver", port=0,
                     options=None, service_args=None,
                     desired_capabilities=None, service_log_path=None,
                     chrome_options=None, keep_alive=True):
            """
            Creates a new instance of the chrome driver.
    
            Starts the service and then creates new instance of chrome driver.
    
            :Args:
             - executable_path - path to the executable. If the default is used it assumes the executable is in the $PATH
             - port - port you would like the service to run, if left as 0, a free port will be found.
             - options - this takes an instance of ChromeOptions
             - service_args - List of args to pass to the driver service
             - desired_capabilities - Dictionary object with non-browser specific
               capabilities only, such as "proxy" or "loggingPref".
             - service_log_path - Where to log information from the driver.
             - chrome_options - Deprecated argument for options
             - keep_alive - Whether to configure ChromeRemoteConnection to use HTTP keep-alive.
            """
            if chrome_options:
                warnings.warn('use options instead of chrome_options',
                              DeprecationWarning, stacklevel=2)
                options = chrome_options
    
            if options is None:
                # desired_capabilities stays as passed in
                if desired_capabilities is None:
                    desired_capabilities = self.create_options().to_capabilities()
            else:
                if desired_capabilities is None:
                    desired_capabilities = options.to_capabilities()
                else:
                    desired_capabilities.update(options.to_capabilities())
    
            self.service = Service(
                executable_path,
                port=port,
                service_args=service_args,
                log_path=service_log_path)
            self.service.start()
    
            try:
                RemoteWebDriver.__init__(
                    self,
                    command_executor=ChromeRemoteConnection(
                        remote_server_addr=self.service.service_url,
                        keep_alive=keep_alive),
                    desired_capabilities=desired_capabilities)
            except Exception:
                self.quit()
                raise
            self._is_remote = False
    

    其构造函数入参executable_path默认为“chromedriver”,在其init方法中,创建了一个Service类实例service,executable_path作为了Service类init方法的入参。executable_path在WebDriver源码中仅仅出现在该处,说明WebDriver在处理executable_path的过程中,仅仅只起到传递值的作用。实际处理executable_path并不在WebDriver。
    进入Service类源码,

    class Service(service.Service):
        """
        Object that manages the starting and stopping of the ChromeDriver
        """
    
        def __init__(self, executable_path, port=0, service_args=None,
                     log_path=None, env=None):
            """
            Creates a new instance of the Service
    
            :Args:
             - executable_path : Path to the ChromeDriver
             - port : Port the service is running on
             - service_args : List of args to pass to the chromedriver service
             - log_path : Path for the chromedriver service to log to"""
    
            self.service_args = service_args or []
            if log_path:
                self.service_args.append('--log-path=%s' % log_path)
    
            service.Service.__init__(self, executable_path, port=port, env=env,
                                     start_error_message="Please see https://sites.google.com/a/chromium.org/chromedriver/home")
    

    init方法中将executable_path传递给了其父类的init方法,整个Service类init方法中仅将executable_path传递给了父类的init方法,未做其他处理。
    进入Service的父类Service源码,

    class Service(object):
    
        def __init__(self, executable, port=0, log_file=DEVNULL, env=None, start_error_message=""):
            self.path = executable
    
            self.port = port
            if self.port == 0:
                self.port = utils.free_port()
    
            if not _HAS_NATIVE_DEVNULL and log_file == DEVNULL:
                log_file = open(os.devnull, 'wb')
    
            self.start_error_message = start_error_message
            self.log_file = log_file
            self.env = env or os.environ
    

    init方法仅仅只是将executable存储下来,存在了self.path中,到此,executable_path的传递过程就结束了,但是我们仍未知道它被用在什么地方。

    既然executable_path被存储在了self.path中,path是Service的一个实例属性,那么它只可能被Service类的方法所使用,回顾先前的代码,WebDriver类创建了Service类的实例service,紧接着就调用了service.start(),进入start方法,

        def start(self):
            """
            Starts the Service.
    
            :Exceptions:
             - WebDriverException : Raised either when it can't start the service
               or when it can't connect to the service
            """
            try:
                cmd = [self.path]
                cmd.extend(self.command_line_args())
                self.process = subprocess.Popen(cmd, env=self.env,
                                                close_fds=platform.system() != 'Windows',
                                                stdout=self.log_file,
                                                stderr=self.log_file,
                                                stdin=PIPE)
            except TypeError:
                raise
            except OSError as err:
                if err.errno == errno.ENOENT:
                    raise WebDriverException(
                        "'%s' executable needs to be in PATH. %s" % (
                            os.path.basename(self.path), self.start_error_message)
                    )
                elif err.errno == errno.EACCES:
                    raise WebDriverException(
                        "'%s' executable may have wrong permissions. %s" % (
                            os.path.basename(self.path), self.start_error_message)
                    )
                else:
                    raise
            except Exception as e:
                raise WebDriverException(
                    "The executable %s needs to be available in the path. %s\n%s" %
                    (os.path.basename(self.path), self.start_error_message, str(e)))
            count = 0
            while True:
                self.assert_process_still_running()
                if self.is_connectable():
                    break
                count += 1
                time.sleep(1)
                if count == 30:
                    raise WebDriverException("Can not connect to the Service %s" % self.path)
    

    在start方法中,主要都是try...except的实现。
    其中将self.path放在了list类实例cmd中,并将cmd作为了参数传给subprocess.Popen函数。而subprocess.Popen函数即相当于在命令行执行了cmd命令。

    所以为selenium的WebDriver init方法配置executable_path可以有两种方法:
    一:将chromedriver的路径加入到环境变量Path中,而不用给WebDriver的构造方法传递任何参数,因为已经有了默认参数“chromedriver”;
    二:不必配置环境变量,直接给WebDriver的init方法传递chromedriver的绝对地址,即WebDriver(executable_path=path);

    相关文章

      网友评论

          本文标题:Selenium源码分析之chromedriver的设置

          本文链接:https://www.haomeiwen.com/subject/kashiqtx.html