selenium 學習記錄:StaleElementReferenceException 的處理

最近在學習 selenium,剛好公司內部有一個課程學習的網(wǎng)站,每個人都需要進去看視頻學習,還會統(tǒng)計學習進度,正好利用 selenium 實現(xiàn)自動掛機學習。

問題

網(wǎng)站頁面有一個課程列表,需要逐個點進去學習。
思路:利用 selenium 找到課程列表元素,通過遍歷找出還沒學習完成的課程,然后逐個點擊課程進行學習。
結果在運行的過程中,經(jīng)常在學習第二個課程的時候報 StaleElementReferenceException 錯誤。

分析

經(jīng)查詢, StaleElementReferenceException 是因為要操作的元素已經(jīng)不在文檔中了,導致操作失敗。
出現(xiàn)這個問題的原因,是因為獲取到這個元素后,頁面可能發(fā)生了以下變化:

  1. 當前瀏覽器的頁面已經(jīng)不是獲取這個元素時的頁面(比如跳轉到了新的頁面),或者頁面刷新了;
  2. 當前元素已被刪除并重新添加回頁面(比如通過 ajax 獲取到新的數(shù)據(jù),然后用 javascript 構建出新的元素并替換掉原來的);
  3. 元素是在 iframe 里面的,而 iframe 刷新了。

對比我的情況,是發(fā)生第 2 種情況:課程的學習進度是每隔一段時間就進行更新,而更新是通過 javascript 替換掉原來元素,所以導致原本獲取到的元素失效了。

解決方法

重新定位元素

經(jīng)過觀察,這個頁面的每個列表元素都有唯一 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()

直接重試

上面的方法經(jīng)使用后,雖然 StaleElementReferenceException 出現(xiàn)的幾率小了,但偶然的情況下還是會繼續(xù)出現(xiàn)。
可能是因為在找元素和點擊的過程間,頁面又再刷新了?
最后實在是沒有辦法,只能通過簡單粗暴的方法解決:捕捉此異常,然后直接重試。
經(jīng)改進后的代碼:

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
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容