web自动化前置准备之selenium原理

selenium是如何操作浏览器的?

我们使用编程语言来写selenium代码,目的是为了让浏览器按照我们的预期来自动化实现一些效果,比如:自动点击、自动输入字符、自动提交表单等。而我们操作网页的方式跟我们实际上网操作的那样,用鼠标定位到需要操作的地方,然后通过鼠标或键盘的一些动作来执行操作;但又有点不太一样,不一样的地方在于,我们定位到需要操作的地方是通过元素,也就是html中对应的标签去定位;而执行操作的方式,就是通过dom对象封装的函数来操作元素。所以,我们通过selenium来使浏览器自动化进行操作,实际上操作的是网页的html(见下图)
在这里插入图片描述

webdriver

python是一门编程语言,html是一门标记性语言,这两个语言是无法互通的。我们只能通过python语言去操作同属于python语言的代码,而不能操作java、php、JavaScript、html等非python语言的代码。所以,我们要想通过python操作html需要借助一些手段,除了要使用selenium模块里丰富的函数外,还需要借助第三方来把selenium写出来的代码解释给html听,然后让html去执行。
我们知道,两个不同的程序要想进行数据通信,就要借助接口。所以python要想跟html进行数据通信,就要通过接口进行数据传输。而这个接口由谁来充当呢?由webdriver(浏览器驱动)来充当。接口一般放在哪呢?放在服务器上。所以webdriver的其中一个作用就是为不同的编程语言与不同浏览器中的html进行数据交互时提供接口(见下图)
在这里插入图片描述
所以,有了webdriver就相当于在编程语言和浏览器之间搭建了一座桥梁,通过编程语言写的每一条selenium脚本,都会变成一个http请求并发送给浏览器驱动,而浏览器驱动中有一个http server负责接收客户端(编程语言写的selenium脚本)传过来的http请求。接收到请求之后,http server根据请求来操作具体的浏览器,如果是chromeDriver,就会取操作chrome浏览器;如果是firefoxDriver,就会去操作火狐浏览器。浏览器执行完相应的步骤之后,就会把执行的结果返回给webDriver中的http server,然后http server再将结果返回给客户端(编程语言),如果执行出错了就会在控制台中打印错误信息。

webdriver协议

上面所说selenium客户端与webdriver通信的协议是http协议,其实selenium把自己又封装了下,写了一大堆的接口,发给了w3c组织并获得许可,这些接口统一起来称为JsonWireProtocol协议。也就是说,selenium写的每一个脚本,背后都会去调用webdriver封装好的接口,通过JsonWireProtocol协议与webdriver进行通信。这也是为什么不同的编程语言能够操作浏览器的根本原因,不管你用什么语言写的selenium代码,都会调用webdriver的接口(也就是走JsonWireProtocol协议)。
以下是webdriver封装的api,详情可参考selenium-github的网址:
https://github.com/seleniumhq/selenium/wiki/jsonwireprotocol
在这里插入图片描述

webdriver与浏览器如何通信?

selenium客户端与webdriver是通过JsonWireProtocol协议进行通信的,那么webdriver又怎么与浏览器进行通信呢?其实webdriver与浏览器也是通过驱动(或者说接口)与浏览器进行通信的,而这个接口就是JavaScript API。

代码层面理解

我们导入webdriver包,然后进入webdriver这个模块看看:
在这里插入图片描述
可以看到这里导入了很多浏览器的驱动,而上面的webdriver.Chrome()其实就是去生成一个chrome下面的webdriver对象。
这里我们进入chrome.webdriver下面的webDriver:
在这里插入图片描述

进入chrome/webdriver.py后,在webdriver类中,调用了一个Service方法,然后又调用了start方法,这里我们进入start方法看下:
在这里插入图片描述
可以看到,这个方法其实是启动一个服务,这个服务就是webDriver,而cmd就是ChromeDriver.exe的所在路径。所以上面的service().start()就是启动ChromeDriver:
在这里插入图片描述
再返回刚刚的代码,上面调用的Service().start()是启动一个webdriver,那下面这段代码是干嘛的呢》这里我们进入ChromeRemoteConnection这个类看看:
在这里插入图片描述
接着再进入它继承的类:RemoteConnection:

class ChromeRemoteConnection(RemoteConnection):

    def __init__(self, remote_server_addr, keep_alive=True):
        RemoteConnection.__init__(self, remote_server_addr, keep_alive)
        self._commands["launchApp"] = ('POST', '/session/$sessionId/chromium/launch_app')
        self._commands["setNetworkConditions"] = ('POST', '/session/$sessionId/chromium/network_conditions')
        self._commands["getNetworkConditions"] = ('GET', '/session/$sessionId/chromium/network_conditions')
        self._commands['executeCdpCommand'] = ('POST', '/session/$sessionId/goog/cdp/execute')

这里代码分为三段。第一段封装了一个commands的列表,这里放入了很多Command类的变量,其实这里就是给每一个selenium的指令指引去访问哪一个接口;第二段封装了一个execute的方法,方法中传入刚刚封装的commands,最后调用了_request方法;最后一段就是_request方法,这里调用了urllib3的PoolManager。这里的urllib3跟requests很类似,都是发起一次http的请求,只不过requests是第三方模块,而urllib3是python的内置模块。最后调用request方法,发起http请求。

       self._commands = {
            Command.STATUS: ('GET', '/status'),
            Command.NEW_SESSION: ('POST', '/session'),
            Command.GET_ALL_SESSIONS: ('GET', '/sessions'),
            Command.QUIT: ('DELETE', '/session/$sessionId'),
  			"""省略下面的代码"""
  			        }

    def execute(self, command, params):
        """
        Send a command to the remote server.

        Any path subtitutions required for the URL mapped to the command should be
        included in the command parameters.

        :Args:
         - command - A string specifying the command to execute.
         - params - A dictionary of named parameters to send with the command as
           its JSON payload.
        """
        command_info = self._commands[command]
        assert command_info is not None, 'Unrecognised command %s' % command
        path = string.Template(command_info[1]).substitute(params)
        if hasattr(self, 'w3c') and self.w3c and isinstance(params, dict) and 'sessionId' in params:
            del params['sessionId']
        data = utils.dump_json(params)
        url = '%s%s' % (self._url, path)
        return self._request(command_info[0], url, body=data)

    def _request(self, method, url, body=None):
        """
        Send an HTTP request to the remote server.

        :Args:
         - method - A string for the HTTP method to send the request with.
         - url - A string for the URL to send the request to.
         - body - A string for request body. Ignored unless method is POST or PUT.

        :Returns:
          A dictionary with the server's parsed JSON response.
        """
        LOGGER.debug('%s %s %s' % (method, url, body))

        parsed_url = parse.urlparse(url)
        headers = self.get_remote_connection_headers(parsed_url, self.keep_alive)
        resp = None
        if body and method != 'POST' and method != 'PUT':
            body = None

        if self.keep_alive:
            resp = self._conn.request(method, url, body=body, headers=headers)

            statuscode = resp.status
        else:
            http = urllib3.PoolManager(timeout=self._timeout)
            resp = http.request(method, url, body=body, headers=headers)
		"""省略后面的代码"""

再返回来看下这段很重要的代码。前面Service().start()是创建一个webdriver服务,打开对应的驱动:ChromeDriver.exe;而下面的RemoteWebDriver其实就是初始化一个selenium的客户端,然后调用webdriver中的接口,向webdriver发起请求。
在这里插入图片描述

生活的例子来理解

在这里插入图片描述

拿乘客、司机、汽车举个例子。乘客上车后,告诉司机去北京路,然后告诉司机怎么走;接着司机操纵汽车,按照乘客的指示驾驶;汽车则受司机方向盘的控制,行驶到具体的地点。
这里乘客就相当于selenium client,也就是各种编程语言,如:java、php、python;selenium则相当于跟司机交谈时的语言,这种语言乘客、司机都能听得懂;司机相当于webdriver,司机也分很多种,有只会驾驶汽车的司机,相当于chromeDriver之于chrome浏览器,也有只会驾驶大巴的司机,相当于firefoxDriver之于火狐浏览器;司机的大脑、手组合则相当于webDriver中的http server;汽车则相当于浏览器。不同国家的乘客(不同的编程语言)都能通过司机(webdriver)来操作汽车(浏览器)来到达他们的目的地(达到他们想要的操作,如点击按钮、提交表单等)。乘客上车后,脑海里想好要去什么地方,怎么去,相当于在写selenium脚本。想好目的地及怎么去之后,就会告诉司机,相当于selenium脚本转化成http请求发送给webDriver。司机的头脑接收到乘客说的话,理解意思并在脑海里规划路线之后,就会用手操作方向盘,驾驶汽车到达乘客指定地点,相当于webDriver中的http server接收到api(编程语言)的http 请求,进行相应的处理之后,就通过json这种通信的数据格式与浏览器进行交互,而浏览器接收到webDriver发过来的json数据后,就会转化成javaScript代码,从而对dom对象进行操作,以达到客户端的目的。司机看到目的地的一个路标之后,就知道到了乘客所说的目的地,然后告诉乘客,目的地到了。这就相当于浏览器在进行完操作之后,就会返回结果给webDriver的http server,接着server再将结果返回给客户端。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值