探索Selenium打开浏览器加载慢的原因

本文探讨了Selenium中使用智能等待和JavaScript的document.readyState属性来判断页面加载完成的策略。通过调整WebDriver的pageLoadStrategy选项,可以实现更精细的页面加载控制,避免元素定位操作前页面未完全加载的问题。

在自动化元素定位操作中经常使用智能等待来加强定位的强壮性,主要就是因为WebDriver没有提供页面加载场景的方法;在使用JavaScript知识的突然心生灵感,可以使用JavaScript来配合验证页面加载,结果发现我真是井底之蛙。

一、domcument.readyState

    首先定位从Document对象出发,而Document对象是在html文档加载完成便可操作使用,所以判断文件装载完成即可;Document对象的readyState属性返回当前文档装载状态:

  1. uninitialized 表示未开始载入
  2. loading 载入中,下载了html并且开始加载
  3. interactive 已加载完html内容
  4. complete 已加载完html内容和下载解析了子资源(js、css、image),不包含ajax异步。

public static void pageDOMLoadComplete(){

       JavascriptExecutor JS=(JavascriptExecutor) SeleniumMethod.ThreadDriver.get();

       try {

              while(!"complete".equals(JS.executeScript("return document.readyState"))){

                     Thread.sleep(2000);

       }

              logger.info("页面元素加载完成");

       } catch (Exception e) {

              // TODO: handle exception

              e.printStackTrace();

              logger.info("判断页面元素加载异常");

       }     

}

    如上代码,如果结果返回的complete表示加载完成,这时候就可以开始元素定位操作。返回不是complete则使用线程循环等待,直到加载完成才跳出循环。

    详细了解的建议去脑补一下浏览器对页面加载的过程。通过浏览器发送请求到服务器响应先得到html形成Document对象,浏览器解析html形成dom树开始加载,当加载dom树遇到外部资源时去请求外部资源,但加载dom树并不会等待会继续加载,当资源如JavaScript、Css、图片等等加载完成这时候document.readyState的值变成complete。

二、Selenium加载策略

    因为Selenium从头到位都没有任何判断页面加载完成的关键字;而结合document.readyState的返回来确定页面加载成功,然后再去执行定位操作,这样来说是个合理的思想。根据想法编写下面的代码:

              WebDriver driver = new ChromeDriver();

              driver.get("https://www.sina.com.cn/");

              try {

 while(!"complete".equals(((JavascriptExecutor)driver).executeScript("return document.readyState"))){

                     Thread.sleep(1000);

              }

              } catch (Exception e) {

                     // TODO: handle exception

                     e.printStackTrace();

              }

              driver.findElement(By.name("SerchKey")).sendKeys("软件测试");

              driver.findElement(By.name("SearchSubButton")).click(); 

              driver.quit();

    上面代码利用JavaScript执行一段脚本document.readyState的值返回不是complete则一直循环并线程等待1S,直到返回的值是complete退出循环。

    但是在代码运行后发现,从来都没有进入过循环。于是猜测,是get()方法在打开浏览器并访问URL时,是不是就已经判断过页面加载完成了?验证方法很简单,把循环去掉,代码直接打印document.readyState返回的值

System.out.print(((JavascriptExecutor)driver).executeScript("return document.readyState"));

    得到的结果每次都是complete,验证确实get()方法后就已经页面加载成功了。在使用WebDriver时,每次打开浏览器都知道它并不会带有任何缓存数据,而是一个全新初始化的浏览器,这点只要用过Selenium的同学,并做过登录功能都应该知道,每次使用都需要重新登录,浏览器并不会保存任何cookie、缓存信息。就是因为这个原因,WebDriver每次调起浏览器并访问URL的时候因为没有缓存所以页面加载会非常慢(当然不仅仅是缓存,WebDriver还会去遍历一些其他东西,如虚拟网卡、代理配置等);这个慢的现象大多数是页面已经展示内容了,然后浏览器还在转圈加载,然而使用document.readyState来判断好像不现实了,因为WebDriver的get()方法默认就会等待页面加载完。所以思路就转化为,get()方法的探究。

    通过官方资料查询,发现WebDriver有pageLoadStrategy加载策略这一说,地址如下:https://www.w3.org/TR/webdriver/#dfn-table-of-page-load-strategies,大家可以自己去瞧瞧。加载策略有如下三种:

  1. none:没有说明对应关系
  2. eager:对应ducument readiness state为”interactive”
  3. normal:对应ducument readiness state为”complete”

    从上面三种加载策略可以看到,官方除了none这种没有具体说明外,另外两种对应的正式document.readyState返回的状态。而默认的加载策略是normal,所以再次验证为什么使用document.readyState得到的值是complete。验证代码如下:

      DesiredCapabilities capabilities = DesiredCapabilities.chrome();

       capabilities.setCapability("pageLoadStrategy", "none");

       WebDriver driver = new ChromeDriver(capabilities);

       driver.get("https://www.sina.com.cn/");

       System.out.println(((JavascriptExecutor)driver).executeScript("return document.readyState"));

       driver.findElement(By.name("SerchKey")).sendKeys("软件测试");

       driver.findElement(By.name("SearchSubButton")).click(); 

       System.out.println(((JavascriptExecutor)driver).executeScript("return document.readyState"));

       driver.quit();

    执行代码,打印document.readyState返回的结果是loading;在使用findElement查找元素时报错“Unable to locate element”。当加载策略为none时,对应document.readyState结果是loading的时候,页面还在加载中,进行元素查找会报错,这很好理解,Dom树还在加载过程中,是无法解析任何元素的查找的,所以报错。

    接下来分别尝试了eager加载策略,需要注意一点的是Chrome浏览器并不支持这种加载策略,所以改为了Firefox验证。

DesiredCapabilities capabilities = DesiredCapabilities.firefox();

capabilities.setCapability("pageLoadStrategy", "eager");

WebDriver driver = new FirefoxDriver(capabilities);

    其他代码不变,执行代码打印document.readyState返回的结果是interactive,但是不同的是使用findElement查找元素没有报错,并且全部执行成功。当加载策略为eager,页面加载结果interactive时,整个DOM树加载完成,这时候就已经可以定位元素并执行操作了;根据document.readyState与WebDriver的加载策略得出以下结论:

(1)loading与none

    DOM树 载入中,也就是说已经下载了html并且开始加载;这时候是无法查找元素的,get()方法的执行时间非常短,可以马上执行get()方法后面的代码。所以这时候就可以使用下面代码再次进行判断后再执行。

while(!"complete".equals(((JavascriptExecutor)driver).executeScript("return document.readyState"))){}

      这样的作法好像与加载策略的默认方法没什么不同,但是WebDriver的get()方法可不仅仅是等待页面为complete而已,它还会去判断一些浏览器其他的配置。

      这种情况还有一种不建议使用的方式,就是使用隐式等待,每次查找元素找不到时,等待一定时间。隐式等待在当前driver的整个生命周期中都生效,是非常适合配合none这种加载策略的。

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); 

(2)interactive与eager

    已加载完html内容,但是没有解析子资源;这时候DOM树加载完成,并且去请求子资源了,是可以查找元素并正确返回了。有个弊端就是如果一些数据是通过JavaScript去赋值的,这时候查找元素某些属性数据可能得到空。

(3)complete与normal

     已加载完html内容和下载解析了子资源(JavaScript、css、image),不包含ajax异步。前面应该都没疑问,重要的是ajax异步请求的加载。如果页面上一些数据是通过ajax异步去请求并加载的,同样在ajax请求中数据响应没有及时回来的情况下,拿到的也是空。当然这并不需要担心,只要不是太大数据请求响应还是非常快速的。

百度阅读:https://yuedu.baidu.com/ebook/82a757ec7e192279168884868762caaedd33baa3

为了提高 Selenium 打开浏览器的速度,可以采取以下几种优化方法: 1. **使用无头模式(Headless Mode)** 无头模式允许浏览器在后台运行,而无需打开图形用户界面。这可以显著减少资源消耗并加快启动速度。例如,使用 Chrome 的无头模式: ```python from selenium import webdriver from selenium.webdriver.chrome.options import Options chrome_options = Options() chrome_options.add_argument("--headless") driver = webdriver.Chrome(options=chrome_options) ``` 2. **禁用不必要的浏览器扩展和功能** 通过禁用不必要的功能(如图片加载JavaScript、GPU 加速等),可以进一步减少浏览器启动时间和资源占用。例如: ```python chrome_options.add_argument("--disable-gpu") chrome_options.add_argument("--blink-settings=imagesEnabled=false") chrome_options.add_argument("--disable-javascript") ``` 3. **使用轻量级浏览器** 与传统的 Chrome 或 Firefox 相比,Chromium 是一个更轻量级的浏览器,适合用于自动化任务。它更新频繁且功能丰富,可以在某些场景下提供更好的性能[^2]。 4. **复用浏览器会话** 如果需要多次执行测试或爬取任务,可以考虑复用现有的浏览器会话,而不是每次都重新启动浏览器。这样可以避免重复的初始化过程。 5. **优化 WebDriver 配置** 确保使用的 WebDriver 版本与浏览器版本兼容,并且尽可能使用最新的版本以获得更好的性能。此外,避免频繁更新驱动程序,选择稳定的版本以减少维护成本[^1]。 6. **使用多线程或多进程** 如果需要并行执行多个任务,可以利用 Python 的多线程或多进程功能来加速脚本的运行。例如,使用 `threading` 模块来启动多个浏览器实例: ```python import threading from selenium import webdriver def open_browser(): driver = webdriver.Chrome() driver.get("http://example.com") threads = [] for _ in range(5): t = threading.Thread(target=open_browser) t.start() threads.append(t) for t in threads: t.join() ``` 7. **选择更高效的工具** 如果 Selenium 仍然无法满足性能需求,可以考虑使用其他更高效的工具,如 Puppeteer 或 Playwright。这些工具提供了更现代的 API,并且在某些场景下比 Selenium 更快。 通过以上方法,可以有效提高 Selenium 打开浏览器的速度,同时减少资源消耗和维护成本。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值