随笔记录方便自己和同路人查阅。
#------------------------------------------------我是可耻的分割线-------------------------------------------
Remote应用
要解释清楚Remote的作用并不太容易,不过我们可以通过分析selenium代码的方式来理解它的作用。我们知道WebDriver支持多浏览器下的执行,这是因为WebDriver针对每一种浏览器驱动都重写WebDriver方法。所以,在脚本运行之前需要先确定浏览器驱动,具体如下:
driver = webdriver.Firefox() driver = webdriver.Chrome() driver = webdriver.Ie()
下面就对这些驱动进行简单分析。
WebDriver驱动分析
在selenium包的WebDriver目录下可以看到如下图所示的目录结构。
查看其中任何一个驱动的目录发现都有一个webdriver.py文件,除了我们熟悉的Firefox、Chrome、IE等驱动外,其中还包括非常重要的remote。从这个角度看,也可以把它看作是一种驱动类型,而这种驱动类型比较特殊,它不是支持某一款特定的浏览器或平台而是一种配置模式,我们在这种配置模式下制定任意的平台或浏览器,这种模式的执行都需要Selenium Server的支持。
打开Selenium包下的webdriver/firefox目录,先看Firefox中webdriver.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_profile和firefox_binary两个参数。而这两个参数分别调用当前目录下的firefox_binary.py和firefox_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类重命名为相应的浏览器(Firefox、Chrome、IE等),所以,在调用不同浏览器的驱动时就简化了层级。
再打开selenium包下的webdriver/chrome目录,查看Chrome中webdriver.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驱动。
通过查看两个文件注意到一个细节,是Firefox和Chrome的WebDriver类都继承RemoteWebDriver类,也就是remote的WebDriver类,那么我们很好奇这个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’ 浏览器(Chrome、Firefox)
‘version’:’’ 浏览器版本。
‘platfrom’:’ANY’ 测试平台(ANY表示默认平台)
‘javascriptEnabled’:’True’ JavaScript启动状态。
‘marionette’:False marionette是Python客户端允许你远程控制基于gecko的浏览器或设备运行一个marionette服务器,包括桌面Firefox和Firefox 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, }