제로베이스 데이터 취업 스쿨 5주차 스터디노트 7호
다운로드가 시작되지 않고, 다운로드 중이라는 모달 창만 떴다.
뭔가 싶어 개발자 도구의 Console 탭을 확인했더니,
웹페이지에서 다음과 같은 에러가 생겼음을 알 수 있었다.
웹페이지의 소스코드를 모두 확인하기는 곤란하지만,
어쨌거나 다운로드가 진행되지 않는 것은 NetFunnel_Action이 정의되지 않았기 때문일 가능성이 생기는 것이다.
(전체 소스코드를 확인하지 못했기 때문에 확실히는 알 수 없다. 소스코드를 모두 읽을 시간도 없고)
어디까지나 가설인 것이다.
이 가설의 설득력을 확인하기 위해서,
NetFunnel_Action을 Console에서 확인해보았다.
위와 같이 NetFunnel_Action이라는 함수를 찾을 수 있다.
따라서, 다운로드 시에는 NetFunnel_Action이라는 함수를 쓰는데,
어떠한 요인에 의해 NetFunnel_Action 함수는 메모리에 올라가는데 일정 시간이 소요되고,
메모리에 올라가기 전에 다운로드 버튼을 눌러 NetFunnel_Action을 호출해서
에러가 발생하는 것이다, 라는 가정을 해볼 수 있다.
사실 다음과 같이 진행을 해도 문제가 없다.
import time
time.sleep(3)
하지만 위의 방식은 DOM과 동기화되지 않는다.
selenium에서 Explicit Waits를 사용하는 장점에 대해 다음과 같이 설명한다.
Since explicit waits allow you to wait for a condition to occur, they make a good fit for synchronising the state between the browser and its DOM, and your WebDriver script.
그러니 sleep을 사용할 때와 동일하게 코드 작성 시간이 소요된다면,
Explicit Waits를 사용하는 편이 프로그램의 확장성을 고려했을 때 더 적합할 수 있다.
(난이도와 1회성 프로그램임을 고려하여 수업에서는 sleep을 활용했을 것이다.)
데이터 수집을 하려면 Selenium과 BeautifulSoup는 거의 필수이고,
향후에 확장성 있는 프로그래밍을 위해서 공부삼아 진행하였다.
그럼 어떻게 코드를 작성하면 될까?
def check_download_ready(driver):
js = "return typeof NetFunnel_Action === \"function\""
return driver.execute_script(js) == True
WebDriverWait(driver, timeout=5).until(check_download_ready)
js 코드는 NetFunnel_Action의 type이 function임을 확인하는 자바스크립트이다.
driver.execute_script(js)를 할 시 자바스크립트 데이터를 파이썬 데이터로 변경해주는 듯 하다.
만약 NetFunnel_Action이라는 함수가 메모리에 올라갔다면 자바스크립트 코드는 true를 반환할 것이다.
그리고 driver.execute_script(js)는 자바스크립트 boolean인 true를
python의 bool인 True로 출력해준다.
그리고 위의 코드를 활용하였더니 정상적으로 다운로드가 진행되어,
가설이 맞을 가능성이 높음을 알 수 있었다.
lambda 함수를 활용하여 다음과 같이 실행할 수도 있다.
js = "return typeof NetFunnel_Action === \"function\""
WebDriverWait(driver, timeout=5) \
.until(lambda d: d.execute_script(js) == True)
내 코드의 경우 select element의 옵션을 밑에서부터 차례대로 다운로드하고 있었다.
차례차례 페이지를 열며 다운로드를 진행하는데,
마지막 강남구의 데이터가 다운로드되지 않는 문제가 있었다.
확인해보니 다운로드가 시작되기 전에 Selenium WebDriver이 종료되어 다운로드 되지 않는듯 했다. (가설)
해당 페이지는 다운로드 시 모달창이 뜨는데,
이를 활용하여 모달창이 뜨고 사라지는 것을 Explicit Waits로 기다리는 코드를 작성하였다.
def wait_modal_twinkle(driver):
modal_window = driver.find_element(By.ID, "modalwindow")
modal_display = expected_conditions.visibility_of(modal_window)
WebDriverWait(driver, timeout=5).until(modal_display)
modal_not_display = expected_conditions.invisibility_of_element(
modal_window)
WebDriverWait(driver, timeout=5).until(modal_not_display)
이 코드를 실행하자 강남구의 데이터를 정상적으로 다운로드하는 것을 확인할 수 있었다.
모달 창을 기다리는 것은 명시적인 접근은 아니다.
내가 원했던 것은 다운로드가 정상적으로 진행되는 것이었으므로,
다운로드 여부로 조건문을 나누어야지 모달창을 기다리는 것은 walkaround라고 볼 수 있다.
차라리 로컬 다운로드 폴더에 python으로 접근하여,
파일 추가 여부를 확인한 후에 진행하는 것이 확실한 접근일 것이다.
또한, 이렇게 진행한다면 프로그램의 테스트 여부를 flag로 전달하여
자동으로 다운로드 파일을 지워주는 기능도 추가할 수 있었을 것이다.
다만 로컬 파일을 어떤 방식으로 기다려야 할지는 의문이다.
로컬은 웹브라우저가 아니니 WebDriverWait를 사용하여 기다릴 수 없다.