selenium 学习记录:StaleElementReferenceException 的处理

最近在学习 selenium,刚好公司内部有一个课程学习的网站,每个人都需要进去看视频学习,还会统计学习进度,正好利用 selenium 实现自动挂机学习。

问题

网站页面有一个课程列表,需要逐个点进去学习。
思路:利用 selenium 找到课程列表元素,通过遍历找出还没学习完成的课程,然后逐个点击课程进行学习。
结果在运行的过程中,经常在学习第二个课程的时候报 StaleElementReferenceException 错误。

分析

经查询, StaleElementReferenceException 是因为要操作的元素已经不在文档中了,导致操作失败。
出现这个问题的原因,是因为获取到这个元素后,页面可能发生了以下变化:

  1. 当前浏览器的页面已经不是获取这个元素时的页面(比如跳转到了新的页面),或者页面刷新了;
  2. 当前元素已被删除并重新添加回页面(比如通过 ajax 获取到新的数据,然后用 javascript 构建出新的元素并替换掉原来的);
  3. 元素是在 iframe 里面的,而 iframe 刷新了。

对比我的情况,是发生第 2 种情况:课程的学习进度是每隔一段时间就进行更新,而更新是通过 javascript 替换掉原来元素,所以导致原本获取到的元素失效了。

解决方法

重新定位元素

经过观察,这个页面的每个列表元素都有唯一 id,而同一元素的 id 即使元素刷新过后还是保持不变的。
因此考虑通过 beautifulsoup 查找出目标元素的 id,再通过 selenium 的 find_element_by_id 方法重新找出元素并进行点击。
这样即使页面刷新了,但还是能通过 find_element_by_id 方法找到最新的元素,而使用 beautifulsoup 查找的效率比直接用 selenium 要高。

while True:
    # 使用 BeautifulSoup 分析
    bs = BeautifulSoup(driver.page_source, 'lxml')
    # 获取目标列表
    sections = bs.select('.chapter-list-box')

    learn_section_id = None
    for section in sections:
        # 获取元素的 id
        id = section.attrs['id']
        state = section.select_one(
            '.section-item > .pointer').text.strip()
        # 找出未完成的课程
        if state == '重新学习':
            continue
        else:
            learn_section_id = id
            break

    if learn_section_id is None:
        break
    else:
        driver.find_element_by_id(learn_section_id).click()

直接重试

上面的方法经使用后,虽然 StaleElementReferenceException 出现的几率小了,但偶然的情况下还是会继续出现。
可能是因为在找元素和点击的过程间,页面又再刷新了?
最后实在是没有办法,只能通过简单粗暴的方法解决:捕捉此异常,然后直接重试。
经改进后的代码:

while True:
    try:
        # 使用 BeautifulSoup 分析
        bs = BeautifulSoup(driver.page_source, 'lxml')
        # 获取目标列表
        sections = bs.select('.chapter-list-box')

        learn_section_id = None
        for section in sections:
            # 获取元素的 id
            id = section.attrs['id']
            state = section.select_one(
                '.section-item > .pointer').text.strip()
            # 找出未完成的课程
            if state == '重新学习':
                continue
            else:
                learn_section_id = id
                break

        if learn_section_id is None:
            break
        else:
            driver.find_element_by_id(learn_section_id).click()
            .......
    except StaleElementReferenceException:
        continue
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。