一文速学-selenium高阶性能优化技巧

前言

最近写的挺多自动化办公的selenium程序没有做优化,执行效率不高,启动浏览器又慢但是又可能出现其他不可控的因素,总结来说虽然放心运行但是又没那么好用,项目是写完了最后还是需要优化结尾的。selenium程序优化空间还是非常大的,根据自己项目的需求来进一步精简,去区分哪些元素是我们要触发的,哪些不需要加载的,将这些优化目标写进去可以大大提高selenium程序的效率。

此篇文章将会将大部分selenium程序优化策略都展示一遍,尽可能将简化涉及到垂直领域的专业知识,转化为大众小白可以读懂易于理解的知识,将繁杂的程序创建步骤逐个拆解,以逐步递进的方式由难转易逐渐掌握并实践,欢迎各位学习者关注博主,博主将不断创作技术实用前沿文章。

在这里插入图片描述

场景优化

一般来说做优化还是根据自己的项目使用场景来做优化,我会举例几个常见的优化场景例子作为实例。

存在动态加载场景

现在基本上都是动态网页,存在比较多页面交互元素,既然大家看到这篇文章想必都是有所需求的,默认大家对selenium使用操作比较熟悉,不熟悉的可以去看看本人之前的文章查漏补缺。等待网页元素加载是需要一定时间的,如果直接接下一步操作可能存在网页元素未完全加载的情况导致报错查找不到对应元素ElementNotInteractableException,这时候可以用到WebDriverWait这个函数:

from selenium.webdriver.support.ui import WebDriverWait

比如我们现在需要等待一个弹窗事件,需要等待几秒的弹出时间,但是时间又是不可控的,可能2s可能5s,那么我们想直到这个元素出现我们才做动作,就可以这么用:

# msg-item是嵌入在iframe里面
        iframe = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.NAME, "private"))
        )

上述代码的含义等待该元素10s,如果出现自动进行下一步操作,如果上述元素没有出现时会报错TimeoutException,一般连着try except做进一步处理。

不使用GUI界面

也就是我们常见的selenium的无头模式,适用于不需要交互的界面测试,尤其是在不需要与页面交互或不关心可视化内容时。一般来说单做网页数据获取功能基本都是用该功能,无头模式下无法可视化查看浏览器操作,需要注意页面的尺寸,以预防元素不可见。

class BasicWebScraper:
    def __init__(self):
        # 配置WebDriver的选项
        self.options = Options()
        # 示例:无头模式运行(不打开浏览器窗口)
        self.options.add_argument("--headless")

设置页面加载策略

一般来说我们要关注的元素并没有那么多,只需要获取到目标元素,只加载局部数据信息就好,不需要把整个页面都加载完整,这时候就以通过设置页面加载策略来优化。页面加载策略主要有以下几种:

  1. normal(默认):等待整个页面加载完成,包括所有的静态资源(如图片、CSS文件)和异步的 JavaScript 脚本。
  2. eager:等待 HTML 完全加载和解析完成,不等待 CSS 文件、图片加载完成,也不等待 JavaScript 脚本异步执行完成。这通常会在 DOMContentLoaded 事件触发后立即发生。
  3. none:仅等待页面开始加载,不等待任何其他内容加载完成。

使用 eagernone 加载策略时,页面上的某些元素可能尚未加载完成,因此在执行与这些元素相关的操作之前,可能需要实施额外的等待策略或检查元素的存在性:

# 初始化 Chrome 选项
options = Options()

# 设置页面加载策略为 'eager'
caps = DesiredCapabilities().CHROME
caps["pageLoadStrategy"] = "eager"  # eager 或 none

# 创建 WebDriver 实例
driver = webdriver.Chrome(desired_capabilities=caps, options=options)

这里详细开展讲述eager模式,很多场景可以直接用eager优化较多时间。设置页面加载策略为 eager 模式意味着 WebDriver 会等待 DOM(文档对象模型)加载完成后立即返回,而不必等待所有相关资源(如样式表、图片、子框架)的加载。一般依赖于页面上的静态资源(如图片和 CSS 文件),则使用 eager 模式可以加快执行速度,比如仅做基础文本页面数据爬虫。

eager优点是如果页面中某些资源加载时间过长,可能导致在 normal 模式下的测试因超时而失败。eager 模式可以减轻这种风险。但缺点也很明显,在 eager 模式下,一些通过 JavaScript 动态生成的元素可能尚未完全加载和渲染,导致自动化脚本可能无法与这些元素交互,可以先测试一下这种模式,确定无误之后可以再用。

禁用图片加载

禁用图片加载可以加快页面加载速度,减少网络流量消耗,适用于不依赖图片的任务,这不仅可以加快页面加载速度,还能减少网络带宽的使用,如果觉得eager一下关停很多加载的时候,如果你的任务不需要用到图片则就可以用此方法:

# 创建 Chrome 选项对象
chrome_options = webdriver.ChromeOptions()

# 禁用图片加载
prefs = {"profile.managed_default_content_settings.images": 2}
chrome_options.add_experimental_option("prefs", prefs)
# 启动带有自定义选项的 Chrome 浏览器
driver = webdriver.Chrome(options=chrome_options)

禁用JavaScript

禁用JavaScript 会影响网页的交互性和动态内容加载。确保任务不需要JavaScript 。Chrome 和 Firefox 等主流浏览器没有提供直接的配置选项来禁用 JavaScript。但是可以通过使用 DevTools Protocol来禁用 JavaScript:

options = Options()
driver = init_driver(options=options)
# 使用 DevTools Protocol 来禁用 JavaScript
driver.execute_cdp_cmd("Emulation.setScriptExecutionDisabled", {"value": True})

之外还有很多方法,比如不要频繁创建和销毁 WebDriver 实例。尽量减少与 DOM 的交互次数。例如,将多次小的 DOM 操作合并为一次更大的操作等。以上场景均为常见的,现在我们再来说细致了解selenium代码编写还可以如何优化。

代码优化

page_source

在代码层面的优化一般都得懂selenium底层运行逻辑,比如解析HTML结构的顺序,查询元素的逻辑,举个简单的例子:我们经常会需要断言页面中的某个部分包含一些具体的文本,下面的语句的输出结果是相同的

driver.page_source 
driver.find_element(:tag_name => ‘body') 

不过对于第二条语句来说,selenium需要去分析页面的结构,最后再找到对应的元素并输入结果,这显然是需要花费时间的。如果页面比较小的化,那么二者的差距可能不大,不过对于大的页面来说,第一条语句速度明显会更快一些。

Method 1: Search whole document text took 0.823076 seconds 
Method 2: Search whole document HTML took 0.039573 seconds

定位精确性

在 Selenium 中,元素的定位精确性可以影响获取元素文本(.text)的速度。:使用精确的选择器(如 ID、ClassName)通常会比使用较复杂的选择器(如 XPath、CSS 选择器)更快。这是因为精确的选择器可以更直接地定位到元素,而复杂的选择器可能需要遍历更多的 DOM 节点。

在使用 XPath 或 CSS 选择器时,最好使用尽可能短的路径。长的或复杂的路径会增加浏览器解析 DOM 的时间,在 XPath 中避免使用通配符(*),并尽量不要定位深层次的嵌套元素,因为这会增加查询的计算负担。每次与 DOM 的交互都会消耗时间,尤其是在复杂或大型的网页上。因此,尽量减少不必要的元素查找和交互。

缓存已查找的元素

对于频繁操作的元素,可以将其存储在变量中,避免重复查找。

# 第一次定位按钮并缓存它
cached_button = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "myButton")))

# 第一次点击按钮
cached_button.click()

需要注意的是,这种方法只适用于页面结构在整个会话中保持不变的情况。如果页面的DOM结构在操作过程中发生了变化(例如,页面部分刷新或完全重新加载),缓存的元素可能会变得过时(stale),此时尝试对其进行操作会导致 StaleElementReferenceException

注意的是,这种方法只适用于页面结构在整个会话中保持不变的情况。如果页面的DOM结构在操作过程中发生了变化(例如,页面部分刷新或完全重新加载),缓存的元素可能会变得过时(stale),此时尝试对其进行操作会导致 StaleElementReferenceException

想要获取更多内容欢迎私聊交流,点关注,防走丢,如有纰漏之处,请留言指教,非常感谢

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐