范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

使用python简单封装selenium常用函数

  前言
  年前走查脚本代码时,发现大家对selenium功能都在重复造轮子,而且容易出现一些常见低级bug。于是在闲暇之余,封装一些常用的selenium功能。一、自动切换frame寻找元素
  在某些网页中,存在多个frame嵌套。而selenium提供的find_element函数只能在当前frame中查找,不能切换到其他frame中,需要从最上级frame中逐步切换(当然也可以指定xpath的绝对路径,但是一般没人这么做)。在我们写代码过程中,需要明确知道当前frame位置和需要寻找元素的frame位置。在frame切换过程中,容易因为疏忽导致frame切换错误导致元素无法找到的bug。
  页面中分布的frame,可以理解为树状结构。因此我们可以采用递归的方式,沿着某条搜索路线frame节点,依次对树中每个节点均做一次访问。
  我们以163网址上的登录框为例:点击登录按钮,弹出登录iframe页面。输入框位置在iframe中,因此我们不能使用xpath获取元素位置,需要进入iframe中,然后获取元素。
  点击登录按钮
  输入框在iframe中
  手动切换ifame可能会产生bug,因此需要一套自动切换和检索frame的机制。具体代码如下:from selenium.webdriver.common.by import By from selenium import webdriver import time   def switch_to_frame(browser, *iframe):     """     :param browser:     :param iframe: iframe的绝对路径     :return:     """     #从最上层开始逐层切入     browser.switch_to.default_content()     for frame in iframe:         browser.switch_to.frame(frame)   def find_element(browser, xpath, *iframe, **kwargs):     """     :param browser:     :param xpath: 元素的xpath     :param iframe: 页面中的iframe路径,在这个iframe中搜索xpath     :param kwargs: 扩展值     :return:     """     # 如果没指定iframe 就进入默认的iframe     if not iframe:         browser.switch_to.default_content()     try:         return browser.find_element(By.XPATH, xpath)     except Exception:         # 如果当前iframe中没有找到元素,则寻找当前页面所有的子iframe,在子iframe中搜寻元素         iframes = browser.find_elements(By.XPATH, "//iframe")         for frame in iframes:             # *(iframe + (frame,) 子iframe的绝对路径             switch_to_frame(browser, *(iframe + (frame,)))             element = find_element(browser, xpath, *(iframe + (frame,)))             if element:                 return element         return None   def login(browser):     find_element(browser, "//a[@id="js_N_nav_login_title"]").click()     time.sleep(3)     ele = find_element(browser, "//input[@data-placeholder="网易邮箱/常用邮箱"]")     print(ele)   if __name__ == "__main__":     browser = webdriver.Edge(executable_path="msedgedriver")     browser.get("https://www.163.com/")     try:         login(browser)     except Exception as e:         print(e.__class__.__name__, e)     browser.quit()
  需要注意的是:如果页面中多个frame中,存在相同的xpath元素。还是需要指定frame的路径,否则会返回搜索到的第一个元素。二、强制等待、隐式等待和显示等待
  强制等待
  直接调用系统time.sleep函数,不管页面加载情况一定会等待指定的时间,即使元素已被加载 。
  1.如果设置的时间较长,会浪费时间
  2.如果设置的时间较短,元素可能没有加载。
  隐式等待
  页面中某元素如果未能立即加载,隐式等待告诉WebDriver需等待一定的时间,然后去查找元素。默认不等待,隐式等待作用于整个WebDriver周期,只需设置一次即可。
  1.在上文的find_element函数中,采用递归方式在所有frame寻找元素。若采用隐式等待,则在每个frame中都需要等待设定的时间,耗时非常长。
  2.某些页面我们想要的元素已经加载完毕,但是部分其他资源未加载。隐式等待必须等待所有元素加载完毕,增加额外等待时间。
  显示等待
  显示等待一般作用于某一个元素,在设定的时间范围内,默认每间隔0.5秒查找元素。返回被加载的元素,若超过设定的时间范围未能查找则报错。显示等待作为selenium常用的等待机制,我们来看下他的源码和机制。
  WebDriverWait初始函数
  driver 注释中解释为WebDriver实例,但是代码中并未有相关检测,因此可以传入任何对象    def __repr__(self):         return "<{0.__module__}.{0.__name__} (session="{1}")>".format(             type(self), self._driver.session_id)
  但是__repr__函数中使用到session_id属性,如果需要显示属性或者转为str对象,最好在driver对象中添加session_id属性
  WebDriverWait的until函数
  在until函数中,我们可以看到driver对象传入method函数。在计时结束前,在不断循环执行method函数,如果method函数有正常返回值则退出循环,否则报TimeoutException错误。from selenium.webdriver.support.wait import WebDriverWait  def test():     return 1  def test2(fun):     print(fun)     return 2+fun()  if __name__ == "__main__":     test.session_id = "123"     print(WebDriverWait(test, 2))     e = WebDriverWait(test, 2).until(test2)     print(e) 输出结果入下:   3
  可以采用装饰器对隐式等待进行封装,这样代码更加精简def wait_time(fun):     def wrapper(*args, **kwargs):         time = kwargs.get("wait_time", 10)         if time:             web_element = WebDriverWait(fun, time).until(lambda x: x(*args, **kwargs))         else:             web_element = fun(*args, **kwargs)         return web_element      return wrapper
  同样的,采用装饰器对其他常用的函数进行封装,例如强制等待、点击、输入文本等。三、解除装饰器
  装饰器虽然很方便,但也会产生一些麻烦。例如在find_element函数递归调用过程中,理应只要执行一次装饰器函数。但因为装饰器已经装饰完毕,导致每次递归都会执行。例如强制等待的sleep函数,如果递归次数越多等待时间越长。
  解除装饰器一般有两种做法:一是约定参数,当递归第二次调用时则不生效。例如def sleep(fun):     def wrapper(*args, **kwargs):         first_sleep = kwargs.get("first_sleep", True)         if first_sleep:             kwargs["first_sleep"] = False             sleep_time = kwargs.get("sleep", 0.2)             time.sleep(sleep_time)         result = fun(*args, **kwargs)         return result     return wrapper
  这种方式实现简单,容易理解。但是增加了参数限制,在fun函数中就不能使用first_sleep参数。
  二是采用装饰器采用wrapped实现,通过访问wrapped属性获得原始函数。例如def sleep(fun):     @wraps(fun)     def wrapper(*args, **kwargs):         sleep_time = kwargs.get("sleep", 0.2)         time.sleep(sleep_time)         result = fun(*args, **kwargs)         return result     return wrapper
  但是某一个函数被多个装饰器装饰时,需要递归解除装饰器。例如def wrapped(fun):     """     解除所有的注解     """     if "__wrapped__" in dir(fun):         fun = fun.__wrapped__         fun = wrapped(fun)     return fun
  最后整体代码如下import datetime import time from selenium.webdriver.common.by import By from datetime import timedelta from selenium import webdriver from selenium.webdriver.remote.webelement import WebElement from functools import wraps from selenium.webdriver.support.wait import WebDriverWait  def sleep(fun):     @wraps(fun)     def wrapper(*args, **kwargs):         sleep_time = kwargs.get("sleep", 0.2)         time.sleep(sleep_time)         result = fun(*args, **kwargs)         return result     return wrapper  def click(fun):     @wraps(fun)     def wrapper(*args, **kwargs):         browser = args[0]         is_click = kwargs.get("click", False)         web_element = fun(*args, **kwargs)         if isinstance(web_element, WebElement) and is_click:             browser.execute_script("arguments[0].click();", web_element)         return web_element     return wrapper  def send_keys(fun):     @wraps(fun)     def wrapper(*args, **kwargs):         keys = kwargs.get("send_keys", "")         web_element = fun(*args, **kwargs)         if isinstance(web_element, WebElement) and keys:             web_element.clear()             web_element.send_keys(keys)         return web_element     return wrapper  def wait_time(fun):     @wraps(fun)     def wrapper(*args, **kwargs):         time = kwargs.get("wait_time", 10)         if time:             web_element = WebDriverWait(fun, time).until(lambda x: x(*args, **kwargs))         else:             web_element = fun(*args, **kwargs)         return web_element     return wrapper  def wrapped(fun):     """     解除所有的注解     """     if "__wrapped__" in dir(fun):         fun = fun.__wrapped__         fun = wrapped(fun)     return fun  def switch_to_frame(browser, *iframe):     #从最上层开始逐层切入     browser.switch_to.default_content()     for frame in iframe:         browser.switch_to.frame(frame)  @sleep @click @send_keys @wait_time def find_element(browser, xpath, *iframe, **kwargs):     # 如果没指定iframe 就进入默认的iframe     if not iframe:         browser.switch_to.default_content()     try:         return browser.find_element(By.XPATH, xpath)     except Exception:         iframes = browser.find_elements(By.XPATH, "//iframe")         for frame in iframes:             switch_to_frame(browser, *(iframe + (frame,)))             element = wrapped(find_element)(browser, xpath, *(iframe + (frame,)))             if element:                 return element         return None  def login(browser):     # 2.通过浏览器向服务器发送URL请求     browser.get("http://192.168.*.*/")     find_element(browser, "//*[@id="loginName"]", send_keys="*****")     find_element(browser, "//*[@id="password"]", send_keys="*****")     find_element(browser, "//*[@id="login"]", click=True)     find_element(browser, "//a[text()="项目个人任务"]", click=True)  if __name__ == "__main__":     browser = webdriver.Edge(executable_path="msedgedriver")     browser.maximize_window()     try:         login(browser)     finally:         time.sleep(6)         browser.quit()结束语
  这次的封装其实还存在很多问题
  1.find_element函数不仅仅只是提供查找元素功能,还提供一些其他功能,因此叫element_operation更为合适。
  2.find_element函数的参数过多,并且很多参数的使用并不在函数本身中,对代码阅读很不友好。
  3.得小心避免参数重复问题,假设装饰器sleep和装饰器wait_time都使用time这个参数,将无法区分具体是哪个函数使用。
  4.不利于扩展和维护,当功能过多时find_element的参数过于庞大。
  如果只是简单地封装和使用,上面这种方式也能达到较好的效果。如果想进一步封装,建议采用链式调用方式,装饰器辅助封装。例如 find_element(browser, "//*[@id="login"]")    .sleep(3)    .click()    .send_keys("123456")
  这样函数的扩展性和可阅读性有较大的提升

选择自动洗碗机的经验知识我们不仅要注重价格,还要注重设备的性能,配置和功能以及实际操作等。洗碗餐具消毒设备有效果吗?一套要多少钱?想开一个餐具消毒公司的人一开始都会提这样的问题。首先,需要根据各自的情况使商用自动洗碗机洗碗效果怎么样?商用洗碗机有平铺式和长龙式,因此,商用洗碗机受到许多酒店,酒店,学校和其他餐饮公司的青睐。虽然商用洗碗机对于餐具的不同规格大小,不同的材料可以清洁,但由于清洁差异,效果不同。平铺式深圳车展前,奥迪新车先预热,90后车主真香新车新闻作为奥迪家族的颜值王,第一代奥迪A5便获得了由德国联邦经济和技术部颁发的最高级别的官方设计奖,此奖在业界享有着设计界的奥斯卡之称。新奥迪A5的外观更具运动感。钛黑蜂窝状单幅让自驾游变成真正的享受,与别克昂科旗一起沉浸在重庆的冬天里时光飞快,又近年关,距离上一个备受关注的冬天,已经整整一年时间。2020年实鼠不易,每年至少一游的计划也因此一直搁置着,然而,想出去走走的想法一直都没有停止。于是,我与老婆商量,在大众在广东为什么卖不好,口碑坏了要花的成本超出想象在疫情期间的2020年,广东全年卖出2168760辆乘用车,比第二名的江苏多了632798辆,在广东排前十名的销量车型全是日系品牌车型,2020年全国总销量第一的一汽大众,销量最好棋逢对手or差异明显?主流合资B级车最强对决每到年底,都会迎来新一轮的购车热潮,辛苦工作一年,买辆车来犒劳下自己,过年回家面对亲戚朋友也更体面,加之年底的金融政策也会相对更加给力,很多消费者都会选择在这个时间段来购车。既要照定制超过300万宾利,只有想不到的,没有做不到的2021年上海车展前,宾利发布第三代全新欧陆GTspeed,这款双门四座超豪跑车搭载了6。0公升W型TSI12缸发动机,在双涡轮增压器的压榨下,最大马力达到惊人的659匹马力,比上五一长假来了,劳斯莱斯库里南推出旅行套装,绝对是奢侈旅行必备库里南绝对是劳斯莱斯车系中的另类,于2018年上市,当年也是劳斯莱斯年轻化的纪年,但是其骨子里还是一辆超豪华品牌的SUV。SUV就是一辆多用途汽车,库里南也不例外,穿州过省长途旅行聆赏爱乐之声,天籁新年音乐会献礼鹏城行业动态2021年1月3日,天籁新年音乐会在深圳保利剧院激情奏响,东风日产天籁携手中国爱乐乐团,为逾千名听众奉上了一场力量与温度交融的饕餮视听盛宴,以无限的欢欣和勇气,致敬2020终于出来了,搭载激光雷达的小鹏P5才是真正意义上的自动驾驶汽车2021年4月19日今日,小鹏汽车携旗下第三款产品,全球首款搭载激光雷达的量产智能汽车小鹏P5亮相2021上海车展,小鹏汽车董事长CEO何小鹏与所有到场嘉宾一起分享如何让智能汽车更买菜吗?电动小车怎么样!几万块,上绿牌还不排队新车短测去年十月,我在七星湾我把零跑T03(简称T03)的动力榨干,对它的动力与操控有了很深刻的印象,这次来个三天的亲密测试,好不好用试试才知道。大家都知道在城市开车道路拥挤不算苦
合资轿车天花板,上汽奥迪A7L开启预售,定价却是自寻短见?文岸青上汽奥迪作为全新的合资车企,自成立以来就备受关注,大家纷纷期待他会有着怎么样的表现,如今一胎终于缓缓落地,五年心血打造出的上汽奥迪A7L,作为最高级别的国产奥迪轿车,给众人带丰田再出故障,全球召回超11万辆汽车,却把中国排除在外?文岸青丰田作为老牌日系车企,因为质量过关而受到了无数人追捧,一直以来有着开不坏的称号,但最近,我们却数次发现它被召回的消息,这是怎么回事?被召回的车辆又有哪些?今天,就让我们一起来通递驿优助力末端数智化发展,构建快递行业新生态我国快递末端配送服务的现状和问题随着我国电子商务的迅猛发展以及电商技术的不断更新,我国的快递业务量不断激增,而且随着消费者的消费水平提升,对快递配送的质量和时效性也有了新的要求。但派费上涨释放了什么信号?你将如何应对这一次的风口?最近,快递行业特别的热闹。国家出台了相应的政策加大对快递行业的支持,7月初,国家邮政局等七部门联合印发关于做好快递员群体合法权益保障工作的意见,切实制定政策维护快递员群体合法权益。妹子的审美真让人捉摸不透,这条裤子真的很美吗?哈哈哈哈哈哈有一句话叫做女为悦己者容,但实际上,现在的女孩子自我意识非常强,他们不会取悦别人,所以在挑选衣服的时候,只要自己喜欢就可以了。还有人认为女孩子穿衣打扮比较奇怪,但是人与人之间的审美不同男生喜欢不同风格的女生,你喜欢穿帆布鞋还是高跟鞋的女孩呢不同的男生会喜欢不同风格的女生,比如有些男生喜欢有气质的,那么这样的女孩大多都会穿高跟鞋,女人味十足,还有些男生喜欢运动阳光的女孩,那么高跟鞋就很少会出现在他们的视野,更多的是运动开启认知智能战略,加速爱数成为大数据基础设施领航者从2006年成立推出数据备份的传统业务,到2021年最新发布认知智能战略。现在,爱数已经转型到了大数据基础设施提供商的全新定位,并立志成为该行业的领航者。历经十五年的风风雨雨,对于明明收购旧金饰更麻烦,金店里的店员为何都喜欢顾客以旧换新?现在人们的生活更美好了。爱美的女士也会购买自己喜欢的首饰来装饰自己。其中,黄金首饰非常受欢迎。这样的首饰既好看又有品位,但仍有升值空间。为什么不买些这种首饰戴着呢?不过,我相信你已两个小孩子老是喜欢抢玩具,妈妈一招化解难题家中两个小孩子,老是抢玩具怎么办呢?聪明的妈妈一招化解难题!这几年,随着二胎政策的实施很多家庭都有了两个小孩。我身边就有很多朋友是三年带两的,也就是说,大宝和二宝年龄相差不到一两岁第五届乌镇互联网大会包志刚大数据和人工智能如何赋能新零售2018年11月7日,由国家互联网信息办公室和浙江省人民政府共同主办的第五届世界互联网大会,在乌镇互联网国际会展中心正式开幕。本届大会以创造互信共治的数字世界携手共建网络空间命运共大数据专家车品觉企业如何存量再造和增量探索直播回看封面车品觉,云徙科技董事数据战略顾问。前阿里巴巴副总裁兼数据委员会会长,国内著名大数据专家,目前为红杉资本中国基金专家合伙人同时在香港北京上海贵阳等多个城市的大数据工作领导小组或大