selenium之等待:WebDriverWait

背景

在selenium模拟网页操作时,由于网页加载和JS运行需要消耗一定的时间,可能会导致元素找不到而出现异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2019-03-18 16:05:21,108 - E:\Python\WorkSpace\firewall\fwConfigGetNew.py[line:80] - ERROR: Message: no such element
(Session info: chrome=42.0.2311.90)
(Driver info: chromedriver=2.14.313457 (3d645c400edf2e2c500566c9aa096063e707c9cf),platform=Windows NT 6.1 SP1 x86_64)
Traceback (most recent call last):
File "E:\Python\WorkSpace\firewall\fwConfigGetNew.py", line 62, in getHuaweiConfig
driver.find_element_by_id("curConfigFileExport-menu-btn").click()
File "C:\Python27\lib\site-packages\selenium-3.0.1-py2.7.egg\selenium\webdriver\remote\webdriver.py", line 269, in find_element_by_id
return self.find_element(by=By.ID, value=id_)
File "C:\Python27\lib\site-packages\selenium-3.0.1-py2.7.egg\selenium\webdriver\remote\webdriver.py", line 752, in find_element
'value': value})['value']
File "C:\Python27\lib\site-packages\selenium-3.0.1-py2.7.egg\selenium\webdriver\remote\webdriver.py", line 236, in execute
self.error_handler.check_response(response)
File "C:\Python27\lib\site-packages\selenium-3.0.1-py2.7.egg\selenium\webdriver\remote\errorhandler.py", line 192, in check_response
raise exception_class(message, screen, stacktrace)
NoSuchElementException: Message: no such element
(Session info: chrome=42.0.2311.90)
(Driver info: chromedriver=2.14.313457 (3d645c400edf2e2c500566c9aa096063e707c9cf),platform=Windows NT 6.1 SP1 x86_64)

思路

手工登陆下这个网址,看了下,打开之后会先出现一个loading提示:

loading

loading结束后才会出现对应的元素按钮:

click

那么就需要想办法延迟,来等待页面加载结束:

方法一: sleep

这是最简单直接的方法,在页面打开之后,增加一个sleep()等待加载。因为这个时间很少会出现加载失败,超过时间仍未加载出来那就当做超时处理。

1
sleep(5)

方法二:WebDriverWait

这是selenium提供的等待方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from selenium.webdriver.support.ui import WebDriverWait
class WebDriverWait(object):
def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None):
"""Constructor, takes a WebDriver instance and timeout in seconds.

:Args:
- driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote)
- timeout - Number of seconds before timing out
- poll_frequency - sleep interval between calls
By default, it is 0.5 second.
- ignored_exceptions - iterable structure of exception classes ignored during calls.
By default, it contains NoSuchElementException only.

Example:
from selenium.webdriver.support.ui import WebDriverWait \n
element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("someId")) \n
is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).\ \n
until_not(lambda x: x.find_element_by_id("someId").is_displayed())
"""

通过传入driver,并设置超时时间t,和循环检查时间p(默认为0.5秒),就可以实现每p秒检查一次是否已经存在对应的元素,一直到超时,存在该元素的话,返回元素。这种方式比较正规,相对方法一会节省一点时间,但其实原理类似。

该方法还需配合另外几个selenium包使用:

1
2
3
4
5
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

els = WebDriverWait(driver, 10, 0.5).until((EC.presence_of_element_located((By.ID, 'topmenu_system'))))

总结

在使用selenium中,由于网络、浏览器、机器配置等因素,时不时就会发生找不到元素的问题,还是要注意多做一些异常处理,在可能涉及到等待的地方参考上述方法等待。如果可以直接使用request请求的话,那还是优先request请求获取相关数据。