史上最强大的python selenium webdriver的包装
1、之前已经发过两次使用单浏览器了,但是这个最完美,此篇并没有使用任何单例模式的设计模式,用了实例属性结果缓存到类属性。
2、最简单的控制单浏览器是只实例化一次类,然后一直使用这个对象,但每个地方运行前必须确保先调用实例化的那段代码,这在多个测试方法之间没有依赖关系时候,并不需要指定先在什么地方实例化。使用一些方法控制,这可以在任何地方实例化无数次都没问题,比判断全局变量为空则实例化弹出浏览器这种方法好用很多。因为此种方法高度封装后,不会在其他类外地方要写代码控制了,在任何地方实例化浏览器都是一样的写法,具有高度的统一性。此类并不是用了单例模式,就算用不用此类,控制一个浏览器不是需要使用什么单例模式,只需要控制driver属性在任何实例中是同一个对象就可以了。
3、如果要使用单例模式来保证那个slenium使之只有一个浏览器,则要小心一个地方,常规型的python3中重写__new__,只能保证每个实例是指向同一个对象,并不能保证只调用__init__一次,需要处理下, 参考
python单例模式控制成只初始化一次,常规型的python单例模式在新式类和经典类中的区别。
放出这个类,用法见测试用例。
1、基于selenium的二次包装,整体代码非常短易于理解,带有两个自定义方法示例供参考,易于扩展。 2、支持无限实例化此类,只要曾经弹出了浏览器,在任何跨函数 跨类 跨python文件中再次实例化此类,绝对不会弹第二个浏览器。所有地方无论怎么写,仍然保持使用同一个浏览器窗口。(当然指的是单次运行python文件,如果自己反复的关闭和启动python,那肯定是不能使用已打开的浏览器的) 3、支持使用自定义的方法名字,同时全所未有的支持了直接性的使用所有此类中没有定义的方法,但在官方类中有的api方法,比如直接支持DriverWrapper().execute_script(script, *args)这种写法。 4、其余想自定义名称的方法可以在这个类下面接着写或者继承这个类再添加其他更多方法
5、支持了一个控制台日志,精确 到文件的名字 类名 是在哪一行打印的日志。
经过测试,支持python2和3,支持chrom和firefox,需要安装selenium包和浏览器驱动放到有环境变量的文件夹下。然后就能运行了。
# coding:utf-8 import logging import unittest from selenium import webdriver class cached_class_property(object): def __init__(self, func): self.func = func def __get__(self, obj, cls): if obj is None: return self value = self.func(obj) setattr(cls, self.func.__name__, value) return value class DriverWrapper(): """ 1、基于selenium的二次封装, 2、支持无限实例化此类,仍然保持使用同一个浏览器窗口。 3、支持使用自定义的方法名字,同时直接性的支持使用所有此类中没有定义的方法,但在官方类中有的api方法,比如直接支持DriverWrapper().execute_script(script, *args)这种写法。 4、其余想自定义名称的方法可以在这个类下面接着写或者继承这个类再添加其他更多方法 """ def __init__(self, driver_name): """ :param driver_name: 浏览器名字数字或者字母 """ self.driver_name = driver_name @cached_class_property def driver(self): driver_var = None if self.driver_name in ['chrome', 1]: driver_var = webdriver.Chrome() if self.driver_name in ['firefox', 2]: driver_var = webdriver.Chrome() return driver_var @cached_class_property def logger(self): logger_var = logging.getLogger(self.__class__.__name__) logger_var.setLevel(logging.DEBUG) stream_handler = logging.StreamHandler() stream_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(filename)s - %(lineno)d - %(levelname)s - %(message)s', "%Y-%m-%d %H:%M:%S")) logger_var.addHandler(stream_handler) return logger_var def open(self, url): self.driver.get(url) def find_element_by_css_selector(self, css_str): # 使用自定义的方法覆盖了原方法,比如先打印出一段话 self.logger.debug('要查找的元素的css选择器是 --> ' + css_str) self.driver.find_element_by_css_selector(css_str) def __getattr__(self, item): # 想把其他的webdriver的操作方法直接添加进来,不一个一个的再写一个方法然后调用driver属性的方法,不想一直搞冗余的代码,可以这么做。python先使用__getattribute__,查不到才会调用__getsttr__方法,利用这个特性,来实现这个添加driver的属性到自己类里面 return getattr(self.driver, item) class _Test(unittest.TestCase): def test(self): driver_wrapper = DriverWrapper(1) driver_wrapper.open('https://www.baidu.com') # 有人不喜欢用get,可以叫open什么的 driver_wrapper.find_element_by_css_selector('#kw') # 当类中存在方法,优先使用了自己类里面的方法,所以每次使用css选择器查找元素时候会打印一个日志 driver_wrapper.find_element_by_id('kw') # 当类中不存在此方法,使用Chrome类的方法 driver_wrapper2 = DriverWrapper(1) # 重新实例化一次这个类,仍然可以接着使用之前的浏览器,不会重新弹一个浏览器窗口 driver_wrapper2.get('https://www.sina.com') # 仍然可以使用get方法打开页面
driver_wrapper.logger.info('运行完了,现在想关闭浏览器') driver_wrapper2.driver.close() # 这样做也可以,但不算是动态添加属性了,这是直接使用的该实例的driver属性的方法,driver属性是Chrome的一个实例。 if __name__ == '__main__': unittest.main()
反对极端面向过程编程思维方式,喜欢面向对象和设计模式的解读,喜欢对比极端面向过程编程和oop编程消耗代码代码行数的区别和原因。致力于使用oop和36种设计模式写出最高可复用的框架级代码和使用最少的代码行数完成任务,致力于使用oop和设计模式来使部分代码减少90%行,使绝大部分py文件最低减少50%-80%行的写法。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」