• (五十八)Selenium Grid2的Remote应用之WebDriver驱动分析


    随笔记录方便自己和同路人查阅。

     

    #------------------------------------------------我是可耻的分割线-------------------------------------------

    Remote应用

    要解释清楚Remote的作用并不太容易,不过我们可以通过分析selenium代码的方式来理解它的作用。我们知道WebDriver支持多浏览器下的执行,这是因为WebDriver针对每一种浏览器驱动都重写WebDriver方法。所以,在脚本运行之前需要先确定浏览器驱动,具体如下:

    driver = webdriver.Firefox()
    driver = webdriver.Chrome()
    driver = webdriver.Ie()

    下面就对这些驱动进行简单分析。

    WebDriver驱动分析

    selenium包的WebDriver目录下可以看到如下图所示的目录结构。

    查看其中任何一个驱动的目录发现都有一个webdriver.py文件,除了我们熟悉的FirefoxChromeIE等驱动外,其中还包括非常重要的remote。从这个角度看,也可以把它看作是一种驱动类型,而这种驱动类型比较特殊,它不是支持某一款特定的浏览器或平台而是一种配置模式,我们在这种配置模式下制定任意的平台或浏览器,这种模式的执行都需要Selenium Server的支持。

    打开Selenium包下的webdriver/firefox目录,先看Firefoxwebdriver.py文件的实现。

    from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
    from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
    
    from .extension_connection import ExtensionConnection
    from .firefox_binary import FirefoxBinary
    from .firefox_profile import FirefoxProfile
    from .options import Options
    from .remote_connection import FirefoxRemoteConnection
    from .service import Service
    from .webelement import FirefoxWebElement
    
    
    class WebDriver(RemoteWebDriver):
    
        # There is no native event support on Mac
        NATIVE_EVENTS_ALLOWED = sys.platform != "darwin"
    
        CONTEXT_CHROME = "chrome"
        CONTEXT_CONTENT = "content"
    
        _web_element_cls = FirefoxWebElement
    
        def __init__(self, firefox_profile=None, firefox_binary=None,
                     timeout=30, capabilities=None, proxy=None,
                     executable_path="geckodriver", options=None,
                     service_log_path="geckodriver.log", firefox_options=None,
                     service_args=None, desired_capabilities=None, log_path=None,
                     keep_alive=True):

    主机查看WebDriver类的__init__()初始化方法,因为Selenium自带Firefox浏览器驱动,所以,这个驱动的重要配置在于firefox_profilefirefox_binary两个参数。而这两个参数分别调用当前目录下的firefox_binary.pyfirefox_profile.py文件,感兴趣的读者可以进一步研究这两个文件的实现。

    我们在脚本中调用Firefox浏览器驱动时的路径为:selenium.webdriver.Firefox(),那么,它是如何指向../selenium/webdriver/firefox/webdriver.py文件中WebDriver类的内?秘密在于../selenium/webdriver/目录下的__init__.py文件。查看__init__.py文件

    from .firefox.webdriver import WebDriver as Firefox  # noqa
    from .firefox.firefox_profile import FirefoxProfile  # noqa
    from .firefox.options import Options as FirefoxOptions  # noqa
    from .chrome.webdriver import WebDriver as Chrome  # noqa
    from .chrome.options import Options as ChromeOptions  # noqa
    from .ie.webdriver import WebDriver as Ie  # noqa
    from .ie.options import Options as IeOptions  # noqa
    from .edge.webdriver import WebDriver as Edge  # noqa
    from .opera.webdriver import WebDriver as Opera  # noqa
    from .safari.webdriver import WebDriver as Safari  # noqa
    from .blackberry.webdriver import WebDriver as BlackBerry  # noqa
    from .phantomjs.webdriver import WebDriver as PhantomJS  # noqa
    from .android.webdriver import WebDriver as Android  # noqa
    from .webkitgtk.webdriver import WebDriver as WebKitGTK # noqa
    from .webkitgtk.options import Options as WebKitGTKOptions # noqa
    from .remote.webdriver import WebDriver as Remote  # noqa
    from .common.desired_capabilities import DesiredCapabilities  # noqa
    from .common.action_chains import ActionChains  # noqa
    from .common.touch_actions import TouchActions  # noqa
    from .common.proxy import Proxy  # noqa
    
    __version__ = '3.14.1'

    通过查看该文件就明白了它的原理,它其实对不同驱动的路径做了简化,并且将不同目录下的WebDriver类重命名为相应的浏览器(FirefoxChromeIE等),所以,在调用不同浏览器的驱动时就简化了层级。

    再打开selenium包下的webdriver/chrome目录,查看Chromewebdriver.py文件的实现。

    import warnings
    
    from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
    from .remote_connection import ChromeRemoteConnection
    from .service import Service
    from .options import Options
    
    
    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):

    同样查看WebDriver类的__init__()初始化方法,因为Selenium模块不自带chromedriver.exe驱动,随意executable_path参数会指定chromedriver驱动。

    通过查看两个文件注意到一个细节,是FirefoxChromeWebDriver类都继承RemoteWebDriver类,也就是remoteWebDriver类,那么我们很好奇这个WebDriver类实现了什么功能呢?

    打开selenium包下webdriver/remote目录下的webdriver.py文件。

    class WebDriver(object):
    
        _web_element_cls = WebElement
    
        def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub',
                     desired_capabilities=None, browser_profile=None, proxy=None,
                     keep_alive=False, file_detector=None, options=None):
            """
            Create a new driver that will issue commands using the wire protocol.
    
            :Args:
             - command_executor - Either a string representing URL of the remote server or a custom
                 remote_connection.RemoteConnection object. Defaults to 'http://127.0.0.1:4444/wd/hub'.
             - desired_capabilities - A dictionary of capabilities to request when
                 starting the browser session. Required parameter.
             - browser_profile - A selenium.webdriver.firefox.firefox_profile.FirefoxProfile object.
                 Only used if Firefox is requested. Optional.
             - proxy - A selenium.webdriver.common.proxy.Proxy object. The browser session will
                 be started with given proxy settings, if possible. Optional.
             - keep_alive - Whether to configure remote_connection.RemoteConnection to use
                 HTTP keep-alive. Defaults to False.
             - file_detector - Pass custom file detector object during instantiation. If None,
                 then default LocalFileDetector() will be used.
             - options - instance of a driver options.Options class
            """
            capabilities = {}
            if options is not None:
                capabilities = options.to_capabilities()
            if desired_capabilities is not None:
                if not isinstance(desired_capabilities, dict):
                    raise WebDriverException("Desired Capabilities must be a dictionary")
                else:
                    capabilities.update(desired_capabilities)
            if proxy is not None:
                warnings.warn("Please use FirefoxOptions to set proxy",
                              DeprecationWarning, stacklevel=2)
                proxy.add_to_capabilities(capabilities)
            self.command_executor = command_executor
            if type(self.command_executor) is bytes or isinstance(self.command_executor, str):
                self.command_executor = RemoteConnection(command_executor, keep_alive=keep_alive)
            self._is_remote = True
            self.session_id = None
            self.capabilities = {}
            self.error_handler = ErrorHandler()
            self.start_client()
            if browser_profile is not None:
                warnings.warn("Please use FirefoxOptions to set browser profile",
                              DeprecationWarning, stacklevel=2)
            self.start_session(capabilities, browser_profile)
            self._switch_to = SwitchTo(self)
            self._mobile = Mobile(self)
            self.file_detector = file_detector or LocalFileDetector()

    WebDriver类的__init__()初始化方法提供了一个重要信息,即command_executor参数,它默认指向本机(127.0.0.1)的444端口号,通过修改这个参数可以使其指向任意的某平台主机。

    除此之外,我们还需要对浏览器进行配置。浏览器的配置由desired_capabilities参数决定,这个参数的秘密在selenium包的webdriver/common目录下的desired_capabilities.py文件中。

    class DesiredCapabilities(object):
    
        FIREFOX = {
            "browserName": "firefox",
            "marionette": True,
            "acceptInsecureCerts": True,
        }
    
        INTERNETEXPLORER = {
            "browserName": "internet explorer",
            "version": "",
            "platform": "WINDOWS",
        }
    
        EDGE = {
            "browserName": "MicrosoftEdge",
            "version": "",
            "platform": "WINDOWS"
        }
    
        CHROME = {
            "browserName": "chrome",
            "version": "",
            "platform": "ANY",
        }
    
        OPERA = {
            "browserName": "opera",
            "version": "",
            "platform": "ANY",
        }
    
        SAFARI = {
            "browserName": "safari",
            "version": "",
            "platform": "MAC",
        }
    
        HTMLUNIT = {
            "browserName": "htmlunit",
            "version": "",
            "platform": "ANY",
        }
    
        HTMLUNITWITHJS = {
            "browserName": "htmlunit",
            "version": "firefox",
            "platform": "ANY",
            "javascriptEnabled": True,
        }
    
        IPHONE = {
            "browserName": "iPhone",
            "version": "",
            "platform": "MAC",
        }
    
        IPAD = {
            "browserName": "iPad",
            "version": "",
            "platform": "MAC",
        }
    
        ANDROID = {
            "browserName": "android",
            "version": "",
            "platform": "ANDROID",
        }
    
        PHANTOMJS = {
            "browserName": "phantomjs",
            "version": "",
            "platform": "ANY",
            "javascriptEnabled": True,
        }
    
        WEBKITGTK = {
            "browserName": "MiniBrowser",
            "version": "",
            "platform": "ANY",
        }

    ‘browserName’:’chrome’  浏览器(ChromeFirefox

    ‘version’:’’  浏览器版本。

    ‘platfrom’:’ANY’  测试平台(ANY表示默认平台)

    ‘javascriptEnabled’:’True’  JavaScript启动状态。

    ‘marionette’:False  marionettePython客户端允许你远程控制基于gecko的浏览器或设备运行一个marionette服务器,包括桌面FirefoxFirefox OS。该参数为Firefox特有。

    DesiredCapabilities平台及浏览器的参数如下

    FIREFOX = {
            "browserName": "firefox",
            "version": "",
            "platform": "ANY",
            "javascriptEnabled":"True",
            "marionette":"False"
        }
    INTERNETEXPLORER = {
            "browserName": "internet explorer",
            "version": "",
            "platform": "WINDOWS",
            "javascriptEnabled":True,
        }
    EDGE = {
            "browserName": "MicrosoftEdge",
            "version": "",
            "platform": "WINDOWS",
        }
    CHROME = {
            "browserName": "chrome",
            "version": "",
            "platform": "ANY",
            "javascriptEnabled":True,
        }
    OPERA = {
            "browserName": "opera",
            "version": "",
            "platform": "ANY",
            "javascriptEnabled":True,
        }
    SAFARI = {
            "browserName": "safari",
            "version": "",
            "platform": "ANY",
            "javascriptEnabled":True,
        }
    HTMLUNITWITHJS = {
            "browserName": "htmlunit",
            "version": "firefox",
            "platform": "ANY",
            "javascriptEnabled":True,
        }
    IPHONE = {
            "browserName": "iPhone",
            "version": "",
            "platform": "MAC",
            "javascriptEnabled":True,
        }
    IPAD = {
            "browserName": "iPad",
            "version": "",
            "platform": "MAC",
            "javascriptEnabled":True,
        }
    ANDROID = {
            "browserName": "android",
            "version": "",
            "platform": "ANDROID",
            "javascriptEnabled":True,
        }
    PHANTOMJS = {
            "browserName": "phantomjs",
            "version": "",
            "platform": "ANY",
            "javascriptEnabled":True,
        }
  • 相关阅读:
    CString详细讲解
    常用的函数调用约定 stdcall/cdecl/fastcall
    near指针,far指针,huge指针
    md /mdd /ml /mt/mtd
    VC通过ADO链接ORACLE数据库
    c++ 线程同步
    typedef struct与struct的区别
    BeginPaint&&GetDc(转)
    left join 命令详解
    sqlserver IO 监测
  • 原文地址:https://www.cnblogs.com/lirongyang/p/12052597.html
Copyright © 2020-2023  润新知