본문 바로가기

프로그래밍/크롤링(PYTHON)

Bot Detection / 크롤러 차단 / 크롤러 우회 #4 (END)

반응형

지난번 포스팅은..

지난번에 포스팅을 급하게 핸드폰으로 했더니 내용도 너무 적고 블로그에서 보니 폰트와 글씨 크기며.. 총체적 난국이었다. 뭐 간편하게 글을 쓸 수 있는 것은 좋긴 한데, 앞으로 핸드폰으로 글을 포스팅할 일은 거의 없지 않을까..

 

ActionChain 2

어쨌든 원래 크롤링 코드에 ActionChain을 적용해봤는데, 결과적으로는 잘 작동했다. 하지만, 시행착오는 언제나 존재하는 법. 그 삽질들을 조금 기록해보고자 티스토리를 열었다.

 

1. StaleElementReferenceException

이름부터 번지르르한 이 친구는 비교적 해결하기 쉬웠다. StaleElementReferenceException (이하 Stale) 은 Web Element[각주:1]에 접근할 때, DOM에 해당 Element가 존재하지 않으면 발생하는 Exception인데... 아래와 같은 상황에서는 일어나기 어렵지만,

 

 
example = driver.findElement(By.id("example"))
print(example.getText())
 

 

만약, 웹페이지를 traversing 하면서 탐색할 때, 사전에 정의해둔 Web Element가 DOM이 완전히 로딩되기 전 참조된다거나, 다른 Webdriver의 Element를 참조할 때 발생하기 쉽다. (후자의 경우는 종종 다른에러가 발생하는데, 밑에서 계속 적을 예정)  

 index = 0
 driver = webdriver.Firefox()
 driver.get('https://www.product_list.com')
 titles = driver.find_elements_by_id('title')
 
 for title in titles:
 	driver.get('https://www.product_detail.com/'+index)
    index += 1
    print(title.text)  

 

제품 목록이 나열된 웹페이지 (https://www.product_list.com) 에서 각 제품 상세정보가 적힌 웹페이지 (https://www.product_detail.com/'+index )로 들어가서 해당 제품명을 출력해주려는 경우 발생할 수 있다. titles는 driver.get('https://www.product_list.com')의 세션에서 생성된 WebElement이기 때문에 다른 세션에서 사용할 수 없다. 사실.. 이게 아직 이해가 잘 안되는데, Selenium에서는 driver.get()으로 어떤 페이지를 열면 driver가 Session을 생성하는데, 이 세션이 페이지를 이동하거나 하면 세션이 바뀌어 버린다. 그래서 해당 Session에서 생성한 WebElement를 사용할 수가 없다. 

 

해결방법

음.. 만약, 페이지가 완전히 로딩되기 전에 WebElement를 너무 일찍 참조하는 것이 문제라면 해결방법은 간단하다. 기다리면 되는 것. 이 기다리는 방법은 지금으로서는 3가지 정도 있다. 

1. time.sleep(sec)

2. driver.implicitly_wait(sec)

3. WebDriverWait(driver, sec).untill(EC.presence_of_located((by.ID, ID)))

1번과 2번은 직관적이다. sec만큼 명시적으로 개발자가 선언한 만큼 강제로 동작을 일시정지한다. 3번은 처음보면 다소 난해한데.. 개념만 보면 가장 이상적인 로딩 대기방법이다. 특정 Element가 로딩되거나, 어떤 상태가 되는 것을 (클릭 가능한 상태, focus된 상태 등) 최대 sec만큼까지만 기다리는 것이다. 어떤 상태가 만족되면 sec를 모두 기다리지 않고도 다음 동작을 수행한다.

3번에 관한 자세한 설명은 다음 링크들을 참조하면 좋을 것 같다.

Selenium Implicitly wait vs Explicitly wait

로딩이 끝날때까지 기다리자 (Waits)

 

 

이어서 계속

  1. driver.find_element_by_id('title') 같이 드라이버를 이용해서 선택한 웹페이지의 엘리먼트를 의미한다. [본문으로]
반응형