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()