[Python]Selenium: StaleElementReferenceException
StaleElementReferenceException
StaleElementReferenceException은 요소가 더 이상 DOM의 존재하지 않거나 업데이트 된 경우에 발생하는 오류입니다.
웹 페이지가 변경되거나 요소가 업데이트될 때 발생할 수 있습니다.
예시 코드
다음은 UNIQLO의 상품 데이터를 크롤링하는 예시 코드입니다.
def crawling():
browser = webdriver.Chrome(options = options)
browser.get(url_list[0])
WebDriverWait(browser, 10).until(EC.presence_of_all_elements_located((By.XPATH,"//*[@id='root']/div[3]/div[2]/div[2]/section[1]/div/section/div/div[2]/div/div/div/div")))
products = browser.find_element(By.CLASS_NAME,"fr-ec-product-collection").find_elements(By.CLASS_NAME,"fr-ec-product-tile-resize-wrapper")
for product in products:
# 제품 상세 URL
url = product.find_element(By.TAG_NAME, "a").get_attribute("href")
browser.get(url)
# browser가 출력될 때까지 대기
WebDriverWait(browser, 20).until(EC.presence_of_element_located((By.CLASS_NAME, "fr-ec-template-pdp")))
code = browser.find_element(By.CLASS_NAME,"fr-ec-template-pdp") \
.find_element(By.CLASS_NAME,"fr-ec-layout") \
.find_element(By.CLASS_NAME,"fr-ec-gutter-container") \
.find_element(By.CLASS_NAME,"fr-ec-caption").text
# 제품 상세 설명 Click
browser.find_element(By.CLASS_NAME,"fr-ec-template-pdp") \
.find_element(By.CLASS_NAME,"fr-ec-layout") \
.find_element(By.CLASS_NAME,"fr-ec-gutter-container") \
.find_element(By.TAG_NAME,"legend").click()
WebDriverWait(browser, 20).until(EC.presence_of_all_elements_located((By.ID,"productLongDescription-content")))
description = browser.find_element(By.CLASS_NAME,"fr-ec-template-pdp") \
.find_element(By.CLASS_NAME,"fr-ec-layout") \
.find_element(By.CLASS_NAME,"fr-ec-gutter-container") \
.find_element(By.ID,"productLongDescription-content") \
.find_element(By.CLASS_NAME,"fr-ec-body").text
print(code)
print(description)
print("============================================================\n")
상품 리스트 페이지에서 products 리스트 변수에 상품 리스트를 담아 넣은 후, products 리스트를 순회하며 product 요소 안 "href" 속성 값을 추출하여 해당 속성 값을 통해 다시 상품 상세페이지로 browser를 열게 됩니다.
위와 같은 상품 상세 페이지에서 상품코드, 상품설명의 text를 가져오게 됩니다. 첫 번째 상품에 대한 정보를 가져온 이후, 두 번째 for 문 돌 때, 문제가 발생하게 됩니다.
이러한 문제가 발생한 이유는 products에 담긴 요소들이 더 이상 유효하지 않기 때문에 "href" 속성을 찾을 수 없게 되었기 때문입니다.
문제를 해결하기 위해선 상품 리스트 페이지로 돌아가 다시 상품 목록에 대한 데이터를 가져와야 합니다.
문제 해결
def crawling():
browser = webdriver.Chrome(options = options)
browser.get(url_list[0])
WebDriverWait(browser, 10).until(EC.presence_of_all_elements_located((By.XPATH,"//*[@id='root']/div[3]/div[2]/div[2]/section[1]/div/section/div/div[2]/div/div/div/div")))
products = browser.find_element(By.CLASS_NAME,"fr-ec-product-collection").find_elements(By.CLASS_NAME,"fr-ec-product-tile-resize-wrapper")
for i in range(len(products)):
product = products[i]
# 제품 상세 URL
url = product.find_element(By.TAG_NAME, "a").get_attribute("href")
browser.get(url)
# browser가 출력될 때까지 대기
WebDriverWait(browser, 20).until(EC.presence_of_element_located((By.CLASS_NAME, "fr-ec-template-pdp")))
code = browser.find_element(By.CLASS_NAME,"fr-ec-template-pdp") \
.find_element(By.CLASS_NAME,"fr-ec-layout") \
.find_element(By.CLASS_NAME,"fr-ec-gutter-container") \
.find_element(By.CLASS_NAME,"fr-ec-caption").text
# 제품 상세 설명 Click
browser.find_element(By.CLASS_NAME,"fr-ec-template-pdp") \
.find_element(By.CLASS_NAME,"fr-ec-layout") \
.find_element(By.CLASS_NAME,"fr-ec-gutter-container") \
.find_element(By.TAG_NAME,"legend").click()
WebDriverWait(browser, 20).until(EC.presence_of_all_elements_located((By.ID,"productLongDescription-content")))
description = browser.find_element(By.CLASS_NAME,"fr-ec-template-pdp") \
.find_element(By.CLASS_NAME,"fr-ec-layout") \
.find_element(By.CLASS_NAME,"fr-ec-gutter-container") \
.find_element(By.ID,"productLongDescription-content") \
.find_element(By.CLASS_NAME,"fr-ec-body").text
print(code)
print(description)
print("============================================================\n")
# 상품 리스트 페이지로 돌아가기
browser.back()
# 다시 제품 목록 로드 대기
WebDriverWait(browser, 10).until(EC.presence_of_all_elements_located((By.XPATH, "//div[contains(@class, 'fr-ec-product-tile-resize-wrapper')]")))
products = browser.find_elements(By.XPATH, "//div[contains(@class, 'fr-ec-product-tile-resize-wrapper')]") # 요소를 다시 찾기
첫 번째 for문을 통해 상품 데이터의 정보(상품코드, 상품설명)를 추출한 후 browser.back()를 통해 다시 상품 리스트 페이지로 돌아가 products 변수에 상품 리스트 정보를 담습니다.
이를 통해 StaleElementReferenceException을 해결할 수 있습니다.