简单的聊聊selenium 底层的原理、不懂这些原理别说你懂自动化。。。。
一、首先实例化浏览器驱动实例:
在浏览器驱动实例selenium做了以下几件事
from selenium import webdriver
import logging
logging.basicConfig(level=logging.DEBUG) # 打印源码中的日志
sp =webdriver.Chrome() #初始化Chrome类复制
Chrome类
class WebDriver(RemoteWebDriver):
# 继承至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):
# executable_path 参数就是chromedriver 驱动的名字在后面会讲到为什么要默认为
# chromedriver
# 其他参数是对浏览器的一些配置这里不详细讲、executable_path参数才是启到关键
.......
# 这里做了一件启动服务的操作
# Service类来自于:
# .chrome.Service
# 每个浏览器都有自己的驱动服务类
self.service = Service(
executable_path, # 为浏览器驱动程序
port=port, # 获取的端口号
service_args=service_args,
log_path=service_log_path # 服务日志文件路径
)
# 开始启动webdriver 服务
self.service.start()
复制
Service 类
Service 类主要是启动webdriver 本地服务的我们看看它内部做了哪些事情。
class Service(object):
def __init__(self, executable, port=0, log_file=DEVNULL, env=None, start_error_message=""):
self.path = executable # 是浏览器驱动程序路径、接收的就是上边的Chrome类
#executable_path 传递过来的
self.port = port # 服务的port
if self.port == 0:
# 如果为默认的0 则进行绑定随机端口
self.port = utils.free_port()
if not _HAS_NATIVE_DEVNULL and log_file == DEVNULL:
log_file = open(os.devnull, 'wb')
self.start_error_message = start_error_message
self.log_file = log_file # 日志文件
self.env = env or os.environ # 获取系统全局变量(系统环境变量)
@property
def service_url(self):
"""
Gets the url of the Service
"""
# 对完整的webdriver http 服务进行拼接
return "http://%s" % utils.join_host_port('localhost', self.port)
def command_line_args(self):
raise NotImplemented("This method needs to be implemented in a sub class")
def start(self):
"""
Starts the Service.
:Exceptions:
- WebDriverException : Raised either when it can't start the service
or when it can't connect to the service
"""
# 开始启动webdriver服务、下面操作其实就是执行了一个shell命令、windows上为dos命令
try:
cmd = [self.path] #拿到浏览器驱动程序
cmd.extend(self.command_line_args())
self.process = subprocess.Popen(cmd, #命令
env=self.env, # 全局变量
close_fds=platform.system() != 'Windows',
# 在windows下关闭dos窗口
stdout=self.log_file,
# 执行输出的内容
stderr=self.log_file,
# 执行时异常内容
stdin=PIPE
# 输入的内容
)
........
复制
free_port方法
def free_port():
"""
Determines a free port using sockets.
"""
# 使用套接字来确定空闲端口
free_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定一个随机的空闲端口
free_socket.bind(('0.0.0.0', 0))
free_socket.listen(5)
# 获取端口号
port = free_socket.getsockname()[1]
# 关闭socket服务
free_socket.close()
return port
复制
其实start方法就是在cmd 做了下面事情
这样就是启动了一个webdriver服务、端口是21111、我们其实可以在浏览器访问的。
127.0.0.1:21111
出现上面内容chromewebdriver 服务已经完成、下一步就selenium中的chrome方法实现的启动浏览器、在启动浏览器前selenium做了一件事就是创建一个session id 用于向webdriver服务发起http请求鉴权指令。
我们打开日志可以查看
http://127.0.0.1:52406/session 该http地址就是创建session 的接口地址、拿到这个session 后就可以在后续对浏览器进行操作、每个操作都是一个http请求、我们只需要启动webdriver服务知道对应的http 接口就可以直接对浏览器进行操作、selenium其实就是使用的webdriver的服务接口对底层进行了封装、暴露出的是封装后的各种语言版本的语法。
webdriver是一种协议、它规定了一组操作浏览器的规范、有了这个规范我们就可以使用不同语言进行封装各种浏览器操作。
其实懂了这些原理我们完全可以自行使用webdriver协议封装类似于selenium client 这样的工具包了。那么你是不是有一定的思路、且打开了对自动化测试的新认知呢?
总结
上面的源码不懂没关系、下面整体的大概总结一下你就懂了基本原理:
1、在我们初始化selenium时selenium client 会进行去启动chromewebdriver.exe(chrome/ie等) 驱动程序
2、浏览器驱动程序通过selenium生成的端口进行启动http webdriver服务、并进行绑定了一个随机的端口号
3、之后selenium client 向webdriver服务发起http请求(路径为http://127.0.0.1:port/session)从webdriver服务生成一个session ID
4、拿到session ID 后 webdriver会进行执行启动浏览器操作、浏览器启动后在每次的浏览器操作时 selenium client 都需要将该session ID 传递给webdriver 服务进行鉴权验证
5、之后每一个selenium 脚本操作都将进行发送指定的http请求到webdriver服务中、webdriver服务进行对指定的脚本操作进行执行、并将执行的结果以http接口 json数据传递给selenium 脚本、并将执行的日志成功或异常抛出到控制台中
我这里也要整理了一些webdriver http 接口
selenium封装的就是这些接口请求。。。