[Python] UI가 없는 웹사이트 크롤링 : JavaScript 함수 직접 호출

지쥬·2025년 7월 22일
0

Python

목록 보기
6/6
post-thumbnail

Qoo10 리뷰 크롤링 시 페이징 버튼이 화면에 표시되지 않는 문제 때문에 기존의 시각적 버튼 클릭 방식 대신 다른 접근 방식을 사용했다.

공부의 축복이 끝이 없어 ~~!!!!!!!

1. 일반적인 페이징 크롤링 방식의 한계

대부분의 웹 크롤링 튜토리얼에서는 find_element로 "다음" 버튼을 찾아 click()하는 방식으로 페이징을 처리한다.

하지만, 특정 웹사이트(예: Qoo10 리뷰 페이지)에서는 총 리뷰 개수는 많은데도 불구하고, 페이지 하단의 "1, 2, 3, 다음"과 같은 페이징 UI 요소가 아예 HTML에 렌더링되지 않는 문제가 발생할 수 있다.

이는 서버 응답 오류, JavaScript 로딩 실패, 또는 특정 조건(예: 검색 결과 없음)에 따른 의도된 동작일 수 있다고 한다.

이러한 상황에서는 WebDriverWait으로 next_button을 아무리 기다려도 찾을 수 없으므로, 기존 방식으로는 다음 페이지로 넘어갈 수 없다 ㅠㅠ

((개발이 빨리 끝날 줄 알았지만 ... 페이징이 안뜨는 이슈를 못찾아서 삽질만 왕창 ..))

2. 웹사이트의 동적 페이징 원리 파악

페이징 UI가 없더라도, 웹사이트는 다음 페이지의 데이터를 어딘가에서 가져와야 한다. 이는 주로 AJAX(Asynchronous JavaScript and XML) 통신을 통해 이루어진다!!

Qoo10 리뷰 페이지의 경우, HTML 소스코드나 JavaScript 이벤트 리스너를 분석해보면 javascript:opinionList(페이지번호); 와 같은 JavaScript 함수 호출이 다음 페이지 로드를 담당한다는 것을 확인할 수 있었다.

<a href="#" onclick="javascript:opinionList(2); return false;">2</a>

3. 해결책: Selenium의 execute_script를 활용한 JavaScript 함수 직접 호출

Selenium WebDriver는 브라우저의 JavaScript 환경에서 임의의 JavaScript 코드를 실행할 수 있는 driver.execute_script() 메서드를 제공한다.

이 메서드를 활용하여, 웹사이트 내부적으로 다음 페이지를 로드하는 데 사용되는 JavaScript 함수(opinionList())를 우리가 직접 호출하는 방식으로 페이지를 이동시킬 수 있다.

driver.execute_script(f"javascript:opinionList({target_page});")

이 방식은 브라우저가 화면에 특정 요소를 렌더링하기를 기다릴 필요 없이, 필요한 액션을 직접 지시하는 방법이다.

4. 구현 상세 (코드에 포함된 주요 로직)

  • 총 리뷰 개수 (opinion_count) 확인:
    • 화면에 페이징 버튼이 없더라도, 총 리뷰 개수는 대부분 표시된다. 이 값을 파싱하여 크롤링해야 할 대략적인 총 페이지 수를 예측한다 (math.ceil(total_reviews / 10)).
    • 이 예측치는 무한 루프에 빠지는 것을 방지하는 중요한 기준이 됩니다.
  • execute_script 호출:
    • while 루프 내에서 현재 page 번호에 1을 더한 target_page를 인자로 opinionList() 함수를 호출한다.
    • driver.execute_script(f"javascript:opinionList({target_page});")
  • 페이지 로드 및 콘텐츠 변경 대기:
    • JavaScript 함수 호출 후에는 AJAX 통신으로 새로운 데이터가 로드될 시간을 줘야 합니다 (time.sleep(2)).
    • 더 확실한 방법은 WebDriverWait을 사용하여 새로운 리뷰 엘리먼트가 로드될 때까지 기다리거나, 이전 페이지의 리뷰 엘리먼트가 사라지는 것(EC.staleness_of)을 기다린다.
  • 무한 루프 방지 로직:
    • last_review_text_on_previous_page 변수를 사용하여, execute_script를 호출했음에도 불구하고 다음 페이지의 첫 번째 리뷰 내용이 이전 페이지의 마지막 리뷰 내용과 동일하다면, 더 이상 새로운 콘텐츠가 없다고 판단하고 크롤링을 종료한다.
    • 미리 정해둔 ABSOLUTE_MAX_PAGES (예: 500페이지) 제한을 두어, 혹시 모를 로직 오류나 웹사이트의 비정상적인 동작으로 인한 무한 루프를 방지한다.
  • finally 블록을 통한 WebDriver 종료:
    • 모든 try...except 블록 바깥의 finally 블록에서 driver.quit()을 호출하여 크롤링 성공 여부나 오류 발생 여부와 상관없이 항상 WebDriver(브라우저)를 깔끔하게 종료하고 리소스를 해제한다. 크롤러의 안정성과 시스템 리소스 관리에 매우 중요하다.

5. 이 방법의 장점과 한계

  • 장점:
    • 시각적 UI 요소에 의존하지 않고 동적 콘텐츠를 로드할 수 있어, 특정 웹사이트의 까다로운 페이징 문제를 해결할 수 있다.
    • 불필요한 UI 상호작용 없이 직접적인 JavaScript 호출로 효율성을 높일 수 있다.
  • 한계/고려사항:
    • 웹사이트의 내부 JavaScript 함수 이름이나 작동 방식이 변경되면 크롤러 코드를 수정해야 한다. (유지보수 필요성)
    • execute_script는 웹사이트의 원래 동작 방식과 다를 수 있으며 문제가 발생할 수도 있다.
    • 무한 루프에 빠지지 않게 방지해야한다.

0개의 댓글