selenium底层原理详解

目录

1、selenium版本的演变

1.1、Selenium 1.x(Selenium RC时代)

1.2、Selenium 2.x(WebDriver整合时代)

1.3、Selenium 3.x +

2、selenium原理说明

3、源码说明

3.1、启动webdriver服务建立连接

3.2、发送操作

1、selenium版本的演变

1.1、Selenium 1.x(Selenium RC时代)

  • 核心原理

    • Selenium RC(Remote Control):Selenium 1.x主要通过Selenium RC来实现自动化测试。Selenium RC启动一个Server,该Server负责控制浏览器行为。
    • JavaScript注入技术:Selenium RC将操作Web元素的API调用转化为JavaScript代码,然后通过Selenium Core(一堆JavaScript函数的集合)注入到浏览器中执行。这种方式依赖于浏览器对JavaScript的支持,但速度较慢且稳定性依赖于Selenium内核对API的JavaScript翻译质量。

1.2、Selenium 2.x(WebDriver整合时代)

核心原理

  • WebDriver的引入:Selenium 2.x整合了WebDriver项目,使得Selenium更加强大。WebDriver利用浏览器原生的API,封装成一套面向对象的Selenium WebDriver API,直接操作浏览器页面里的元素,甚至操作浏览器本身(如截屏、窗口大小调整、启动/关闭浏览器等)。
  • 浏览器原生API:由于使用浏览器原生API,WebDriver的速度大大提升,且调用的稳定性交给了浏览器厂商本身。然而,不同浏览器厂商对Web元素的操作和呈现存在差异,因此WebDriver需要为不同浏览器提供不同的实现(如ChromeDriver、FirefoxDriver等)。
  • WebDriver Wire协议:WebDriver启动后会在特定端口上启动基于WebDriver Wire协议的Web Service,所有对WebDriver的API调用都会通过HTTP请求发送给这个Web Service。

1.3、Selenium 3.x +

核心原理

  • 继承2.x的特性:Selenium 3.x在底层原理上与Selenium 2.x保持一致,继续利用WebDriver和浏览器原生API进行操作。
  • 新增特性:Selenium 3.x加入了对更多浏览器原生驱动的支持,如Edge和Safari的原生驱动,以及更新了对Firefox的支持(通过geckodriver)。
  • 移除Selenium RC:与Selenium 2.x相比,Selenium 3.x去除了Selenium RC组件,更加专注于WebDriver的使用。
  • 新增功能和API:为了满足用户不断变化的需求,Selenium会引入新的功能和API,以支持更复杂的测试场景和用例。

2、selenium原理说明

说明:这里说的原理都是整合了WebDriver之后的selenium版本。

思考:selenium是如何驱动浏览器做各种操作的呢?

  • 分析:
    • 首先我们想想,我们可以直接和浏览器交互吗,显然是不能,这时候就需要借助一个代理人帮我们做这件事,这个代理人就是WebDriver,我们不知道浏览器内核的各种API,难道浏览器厂商还不知道吗,所以他们就提供这样一个代理人给我们使用。
    • 也就是我们现在知道WebDriver提供一个服务,我们去请求这个服务把对浏览器的操作通过HTTP请求发送给WebDriver这个服务,再由它把操作解析后去调用浏览器的API,最终结果原路返回。
    • 这个时候我们还需要把这些操作统一起来才行,不然不太可能我们自己总是去调用接口发送请求吧,这时候selenium client就出现了,它在内部帮我们处理好了底层通信的一切,还把对浏览器的操作统一封装成一个个函数供给我们操作,我们只需要关心操作和操作返回的结果就行。
    • 综上就是整个selenium做的事情了。

把上面的过程在提炼一下,流程如下:

  • 1.对于每一条Selenium脚本,一个http请求会被创建并且发送给浏览器的驱动,最开始建立连接时服务端返回一个sessionid给客户端,后续的交互都是通过sessionid进行交互
  • 2.浏览器驱动中包含了一个HTTP Server,用来接收这些http请求
  • 3.HTTP Server接收到请求后根据请求来具体操控对应的浏览器
  • 4.浏览器执行具体的测试步骤
  • 5.浏览器将步骤执行结果返回给HTTP Server
  • 6.HTTP Server又将结果返回给Selenium的脚本,如果是错误的http代码我们就会在控制台看到对应的报错信息。

3、源码说明

说明:我们从源码的角度看看,底层是如何进行交互的

3.1、启动webdriver服务建立连接

代码如下:

from selenium import webdriverdriver_path = 'E:\PycharmProjects\webUiTest\env\Scripts\chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)

1、我们看看代码webdriver.Chrome(executable_path=driver_path)做了什么事情,按住ctrl键点击Chrome进入源码查看:

 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):if chrome_options:warnings.warn('use options instead of chrome_options',DeprecationWarning, stacklevel=2)options = chrome_optionsif options is None:# desired_capabilities stays as passed inif 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()raiseself._is_remote = False

2、我们知道webdriver.Chrome()就是建立服务连接的过程,所以我们看到建立服务相关的代码就是:

我们在进入到self.service.start()源码看看它做了什么,源码如下:

    def start(self):"""Starts the Service.:Exceptions:- WebDriverException : Raised either when it can't start the serviceor 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:pass

3、原来是通过subprocess.Popen()函数根据我们传过来的chromedriver路径,开启一个子进程来执行打开chromedriver服务的命令。

4、但是别急,到这里只是把webdriver服务开启了,还没有初始化driver对象,继续回到源码,初始化driver对象肯定是在开启服务之后,也就是下面的源码:

5、我们继续进入看看它做了什么事情:

    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."""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_executorif 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 = Trueself.session_id = Noneself.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实例,它将使用WebDriver协议来发送命令给浏览器。
  • 使用对应变量保存相关初始化需要的参数,然后开启一个会话(session)与webdriver建立通信,我们看看最重要的部分,也就是开启会话调用的函数:

    def start_session(self, capabilities, browser_profile=None):"""Creates a new session with the desired capabilities."""if not isinstance(capabilities, dict):raise InvalidArgumentException("Capabilities must be a dictionary")if browser_profile:if "moz:firefoxOptions" in capabilities:capabilities["moz:firefoxOptions"]["profile"] = browser_profile.encodedelse:capabilities.update({'firefox_profile': browser_profile.encoded})w3c_caps = _make_w3c_caps(capabilities)parameters = {"capabilities": w3c_caps,"desiredCapabilities": capabilities}response = self.execute(Command.NEW_SESSION, parameters)

可以看到start_session()函数里面发送请求是:self.execute()函数,我们继续进入看看:

    def execute(self, driver_command, params=None):"""Sends a command to be executed by a command.CommandExecutor."""if self.session_id is not None:if not params:params = {'sessionId': self.session_id}elif 'sessionId' not in params:params['sessionId'] = self.session_idparams = self._wrap_value(params)response = self.command_executor.execute(driver_command, params)if response:print("打印响应参数", json.dumps(response, indent=4))self.error_handler.check_response(response)response['value'] = self._unwrap_value(response.get('value', None))return response# If the server doesn't send a response, assume the command was# a successreturn {'success': 0, 'value': None, 'sessionId': self.session_id}

通过源码的值它主要是通过CommandExecutor发送一个请求,这里我们把响应的结果打印到控制台看看,这里的响应返回了什么,新增一行输出代码如下:

我们在进入到self.command_executor.execute(driver_command, params)函数看看是怎么把请求发送出去的:

 def execute(self, command, params):"""Send a command to the remote server.Any path subtitutions required for the URL mapped to the command should beincluded in the command parameters."""command_info = self._commands[command]assert command_info is not None, 'Unrecognised command %s' % commandpath = 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)

这里还是没有看到它到底是怎么把请求发送出去的,继续进入到self._request(command_info[0], url, body=data)函数:

    def _request(self, method, url, body=None):"""Send an HTTP request to the remote server."""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 = Noneif body and method != 'POST' and method != 'PUT':body = Noneprint(f"请求参数:url: {url} \n body: {body} \n headers: {json.dumps(headers, indent=4)}")if self.keep_alive:resp = self._conn.request(method, url, body=body, headers=headers)statuscode = resp.statuselse:http = urllib3.PoolManager(timeout=self._timeout)resp = http.request(method, url, body=body, headers=headers)

终于到这里看到了它底层是通过urllib3库来发送http请求的,这里我们把请求的参数打印出来:

我们再次运行下面的代码,看看请求参数和响应结果是什么:

from selenium import webdriverdriver_path = 'E:\PycharmProjects\webUiTest\env\Scripts\chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)

输出结果如下:

请求参数:url: http://127.0.0.1:59146/session body: {"capabilities": {"firstMatch": [{}], "alwaysMatch": {"browserName": "chrome", "platformName": "any", "goog:chromeOptions": {"extensions": [], "args": []}}}, "desiredCapabilities": {"browserName": "chrome", "version": "", "platform": "ANY", "goog:chromeOptions": {"extensions": [], "args": []}}} headers: {"Accept": "application/json","Content-Type": "application/json;charset=UTF-8","User-Agent": "selenium/3.141.0 (python windows)","Connection": "keep-alive"
}
打印响应参数 {"value": {"capabilities": {"acceptInsecureCerts": false,"browserName": "chrome","browserVersion": "127.0.6533.100","chrome": {"chromedriverVersion": "127.0.6533.119 (bdef6783a05f0b3f885591e7d2c7b2aec1a89dea-refs/branch-heads/6533@{#1999})","userDataDir": "C:\\Users\\\u5218\u519b\\AppData\\Local\\Temp\\scoped_dir13212_999079333"},"fedcm:accounts": true,"goog:chromeOptions": {"debuggerAddress": "localhost:59154"},"networkConnectionEnabled": false,"pageLoadStrategy": "normal","platformName": "windows","proxy": {},"setWindowRect": true,"strictFileInteractability": false,"timeouts": {"implicit": 0,"pageLoad": 300000,"script": 30000},"unhandledPromptBehavior": "dismiss and notify","webauthn:extension:credBlob": true,"webauthn:extension:largeBlob": true,"webauthn:extension:minPinLength": true,"webauthn:extension:prf": true,"webauthn:virtualAuthenticators": true},"sessionId": "34680a6d180d4c8f0a7225d00f92111f"}
}

终于我们可以看到初始化建立会话是通过请求url: http://127.0.0.1:59146/session  然后返回sessionId,后续操作都会携带该sessionId请求,这样对应webdriver它才知道来自那个请求,从而实现会话保持,至此终于把会话建立了,后续就可以通过这个会话发送操作了。

3.2、发送操作

前言:通过上面的终于把会话建立了,现在我们就需要通过会话发送操作命令了,我们执行下面的代码看看这个过程是怎么的:

driver.get("https://www.baidu.com")

运行结果如下:

driver.get():做的事就是把get操作转换为对应的url地址,然后通过携带sessionId发送请求到webdriver服务端,也就是说driver.xxx()的每一个操作都对应了一个url地址,这里肯定有个映射关系来维持,进入源码查看不难找到在RemoteConnection这个类中维护了这样的关系:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/404452.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【性能优化】修复一个谷歌官方承认的内存泄漏问题

前言 通过下面这段代码&#xff0c;配合控制台可以直观看到谷歌官方承认的一个内存泄漏问题&#xff0c;https://issues.chromium.org/issues/41403456。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta …

前端css动画transform多个属性值写法

X轴平移400px transform: translateX(400px); X轴平移400px并缩小0.5倍 transform: translateX(400px) scale(0.5); X轴平移400px并旋转45度 transform: translateX(400px) rotate(45d…

备考2024年美国数学竞赛AMC10:吃透1250道真题和知识点(持续)

有什么含金量比较高的初中生数学竞赛吗&#xff1f;美国数学竞赛AMC10是个不错的选择。那么&#xff0c;如何备考AMC10美国数学竞赛呢&#xff1f;做真题&#xff0c;吃透真题和背后的知识点是备考AMC8、AMC10有效的方法之一。 通过做真题&#xff0c;可以帮助孩子找到真实竞赛…

基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(二)---ROS2与UE5进行图像数据传输

前言 本系列教程旨在使用UE5配置一个具备激光雷达深度摄像机的仿真小车&#xff0c;并使用通过跨平台的方式进行ROS2和UE5仿真的通讯&#xff0c;达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础&#xff0c;Nav2相关的学习教程可以参考本人的其他博…

系规学习第13天

1、规划设计的主要目的不包括() A、设计满足业务需求的IT服务 B、设计SLA、测量方法和指标。 C、设计服务过程及其控制方 D、设计实施规划所需要的进度管理过程 [答案] D [解析]本题考察的是规划设计的目的&#xff0c;建议掌握。 (1)设计满足业务需求的IT服务。 (2)设…

Axios请求使用params参数导致后端获取数据嵌套

问题重述&#xff1a; 首先看前端的axios请求这里我使用params参数将data数据传给后端 let data JSON.stringify(this.posts);axios.post("/blog_war_exploded/insertPost", {params: {data: data}}).then((res) > {if (res.data "success") {alert(…

大杂烩!注意力机制+时空特征融合!组合模型集成学习预测!CNN-LSTM-Attention-Adaboost多变量负荷预测

大杂烩&#xff01;注意力机制时空特征融合&#xff01;组合模型集成学习预测&#xff01;CNN-LSTM-Attention-Adaboost多变量负荷预测 目录 大杂烩&#xff01;注意力机制时空特征融合&#xff01;组合模型集成学习预测&#xff01;CNN-LSTM-Attention-Adaboost多变量负荷预测…

银河麒麟V10如何安装本地deb软件包?(以安装wps为例)

银河麒麟V10如何安装本地deb软件包&#xff1f;&#xff08;以安装wps为例&#xff09; 一、准备二、安装三、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在银河麒麟V10中安装本地.deb软件包&#xff0c;虽然apt主要用于管理仓库中…

LeetCode:3148. 矩阵中的最大得分(DP Java)

目录 3148. 矩阵中的最大得分 题目描述&#xff1a; 实现代码与解析&#xff1a; DP 原理思路&#xff1a; 3148. 矩阵中的最大得分 题目描述&#xff1a; 给你一个由 正整数 组成、大小为 m x n 的矩阵 grid。你可以从矩阵中的任一单元格移动到另一个位于正下方或正右侧…

删除微博博文js脚本实现

我当前的时间&#xff1a;2024.8.18 脚本可以直接使用&#xff0c;随着时间推移&#xff0c;微博页面元素可能会有变动。 思路&#xff1a;javascript 模拟手动点击&#xff0c;下滑&#xff0c;并且删除博文 首先登录微博&#xff0c;进入自己的博文界面如下&#xff1a; 进…

Git使用方法(三)---简洁版上传git代码

1 默认已经装了sshWindows下安装SSH详细介绍-CSDN博客 2 配置链接github的SSH秘钥 1 我的.ssh路径 2 进入路径cd .ssh 文件 3 生成密钥对 ssh-keygen -t rsa -b 4096 (-t 秘钥类型 -b 生成大小) 输入完会出现 Enter file in which to save the key (/c/Users/Administrator/…

使用DOM破坏启动xss

目录 实验环境&#xff1a; 分析&#xff1a; 找破坏点&#xff1a; 查看源码找函数&#xff1a; 找到了三个方法&#xff0c;loadComments、escapeHTM 、displayComments loadComments escapeHTM displayComments&#xff1a; GOGOGO 实验环境&#xff1a; Lab: Exp…

MySQL库表的基本操作

目录 1.库的操作1.1 创建数据库1.2字符集和校验规则①查看系统默认字符集以及校验规则②查看数据库支持的字符集③查看数据库支持的字符集校验规则④校验规则对数据库的影响 1.3操纵数据库①查看数据库②显示创建的数据库的语句③修改数据库④数据库删除⑤备份和恢复⑥还原注意…

C库函数signal()信号处理

signal()是ANSI C信号处理函数&#xff0c;原型如下&#xff1a; #include <signal.h>typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); signal()将信号signum的处置设置为handler&#xff0c;该handler为SIG_IGN&#xff…

物联网(IoT)详解

物联网&#xff08;IoT&#xff09;详解 1. IoT定义简介2. IoT工作原理3. IoT关键技术4. 物联网与互联网区别5. IoT使用场景6. 开源物联网平台7. 参考资料 1. IoT定义简介 首先第一个问题&#xff0c;什么是物联网&#xff08;IoT&#xff09;? 物联网&#xff08;英文&#…

LabVIEW光纤水听器闭环系统

开发了一种利用LabVIEW软件开发的干涉型光纤水听器闭环工作点控制系统。该系统通过调节光源频率和非平衡干涉仪的光程差&#xff0c;实现了工作点的精确控制&#xff0c;从而提高系统的稳定性和检测精度&#xff0c;避免了使用压电陶瓷&#xff0c;使操作更加简便。 项目背景 …

thinkphp5实现弹出框(下拉框选项动态赋值)

效果图 原理 先执行接口获取动态数据&#xff0c;然后在 layer.open的success回调函数中动态添加html代码片段&#xff0c;通过如下方法&#xff0c;将动态生成的代码插入指定的div中&#xff0c;实现动态赋值的效果。 // 动态获取的数据 var data ......;// 弹出框配置 lay…

【BUU】[NewStarCTF 2023 公开赛道]Final -CP读取文件内容

漏洞检测 访问首页发现是ThinkPHP5 的站点 用工具扫描一下,发现存在ThinkPHP5.0.23 RCE漏洞 访问验证,写入shell 成功写入shell. 根目录发现flag,但是权限不足 提权获取flag 准备提权,这里一开始尝试了find,但是find权限不足 尝试采用cp命令,移动到web目录,发现访问还是…

基于web的物流管理系统--论文pf

TOC springboot473基于web的物流管理系统--论文pf 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往往是人们思想上不可…

基于深度学习的图像特征优化识别复杂环境中的果蔬【多种模型切换】

文章目录 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍图像特征优化方法模型原理及实验对比模型训练每文一语 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主 项目介绍 基于深度学习的图像识别技术广泛应…