(五十八)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,
    }
原文地址:https://www.cnblogs.com/lirongyang/p/12052597.html