Stage4 - Selenium 데이터수집기 완성하기

selenium을 활용해서 네이버지도 수집기를 만들었지만 아직 완벽한 데이터수집기는 아닙니다. 마지막 스테이지에서는 부족한 부분을 완성하여 데이터를 끝까지 수집할 수 있는 데이터수집기를 완성합니다.

현재 네이버 지도 데이터 수집에서 부족한 점

6페이지 이후의 데이터를 수집하기 어렵습니다.

현재 페이지를 넘기는 방법(range를 활용한 반복)으로는 6페이지 이 후의 데이터를 수집할 수 없습니다. 앞에서 수집한 page_bar에는 6페이지 이후의 페이지를 선택할 수 있는 방법이 없기 때문입니다. 뒷 페이지의 데이터를 계속해서 수집하려면 5의 배수 페이지를 수집한 이후에는 > 모양의 다음 페이지 버튼을 클릭해줘야합니다.

전화번호가 없는 경우 에러가 발생합니다.

네이버 지도에는 종종 전화번호가 없는 가게가 등록되어 있는 경우가 있습니다. 이때는 selenium에서 전화번호(dd.tel)을 찾을 수 없기 때문에 no such element(그런 데이터 없어!) 에러가 발생합니다.

다음페이지가 없는 경우 에러가 발생합니다.

다음 페이지가 없는 경우(데이터를 모두 수집한 경우)에는 더이상 다음 버튼을 찾을 수 없기 때문에 IndexError: list index out of range (page_bar 리스트에 해당 인덱스가 없어!)에러가 발생합니다.

네이버 데이터 수집기 완성하기

계속해서 페이지 넘기기(6페이지 이후)

앞에서 살펴본대로 한 범위반복을 통해서 6, 7, 8, ... 페이지를 선택할 수 없기 때문에 현재 페이지에 따라 다른 버튼을 클릭해야합니다.

  1. 현재 페이지(n)가 5의 배수가 아닌 경우에는 n+1 페이지 버튼을 클릭해줍니다.

  2. 현재 페이지(n)가 5의 배수인 경우에는 다음페이지(>) 버튼을 클릭해줍니다.

지난 시간에 공부한 조건문(if)을 활용하면 현재 페이지에 따라 다른 코드를 실행할 수 있습니다. 5의 배수는 2주차에서 배운 %연산자를 활용하면 쉽게 판별할 수 있습니다.

%연산자를 통해 나머지 값을 구해낼 수 있으므로 n을 5로 나눈 나머지가 0이라면(5의 배수인 경우) n+1페이지 버튼 클릭, 0이 아니라면(5의 배수가 아니라면) > 페이지 버튼 클릭하면 됩니다.

아래 코드와 같이 % 연산자를 활용하여 조건문을 적용하면 뒷 페이지의 데이터도 수집되는 것을 확인할 수 있습니다.

week6_2.py
... 생략
##################################################
# 범위를 크게 증가시켜줍니다.(20페이지까지)
for n in range(1, 20):
##################################################
time.sleep(1)
# 컨테이너(가게 정보) 수
stores = driver.find_elements_by_css_selector("div.lsnx")
for store in stores:
# 세부 데이터 수집
name = store.find_element_by_css_selector("dt > a").text
addr = store.find_element_by_css_selector("dd.addr").text
phone = store.find_element_by_css_selector("dd.tel").text
print(name, addr, phone)
page_bar = driver.find_elements_by_css_selector("div.paginate > *")
##################################################
# % 연산자를 활용해서 조건문을 추가합니다.
# 현재 페이지(n)가 5의 배수가 아닌 경우 n%5+1 페이지 버튼 클릭
if n%5 != 0:
page_bar[n%5+1].click()
# 현재 페이지(n)가 5의 배수인 경우 다음페이지 버튼을 클릭
else:
page_bar[6].click()
##################################################

전화번호가 없는 경우 에러처리

전화번호가 없는 경우의 에러처리는 4주차에서 다룬 try/except 문을 활용해서 간단히 처리할 수 있습니다. 에러메시지를 살펴보면 데이터를 수집해서 텍스트로 변경해주는 부분에서 에러가 발생하고 있으므로 해당하는 코드 부분을 에러처리 해줍니다.

phone = store.find_element_by_css_selector("dd.tel").text
# 전화번호 수집을 시도합니다.
try:
phone = store.find_element_by_css_selector("dd.tel").text
# 전화번호 수집에서 에러가 발생하면 phone에 "전화번호 없음"을 저장합니다.
except:
phone = "전화번호 없음"

이때 유의할 점은 except 구문에서 전화번호를 대체할 문자열을 넣어주는 것입니다. except 구문에 데이터를 넣어주지 않는다면 뒤에서 데이터를 저장 또는 출력 시에 다시 에러가 발생하게 됩니다.

다음 페이지가 없는 경우 에러처리

수집하려는 페이지의 다음 페이지가 없는 경우에도 역시 에러처리를 해줘야합니다. 이 때는 다음 페이지를 클릭하는 부분에서 에러가 발생하므로 다음페이지를 클릭하는 코드 전체를 에러처리해줍니다.

if n % 5 != 0:
page_bar[n % 5 + 1].click()
else:
page_bar[6].click()
try:
if n % 5 != 0:
page_bar[n % 5 + 1].click()
else:
page_bar[6].click()
except:
print("데이터 수집 완료")
break

다음 페이지가 없는 경우는 더 이상 수집할 페이지가 없다는 의미이므로 데이터 수집이 완료되었다는 것을 의미합니다. 이 때는 break문을 삽입하여 현재 반복되고 있는 반복문을 멈춰줍니다.

# break문을 통해 정지되는 반복문
for n in range(1, 20):

break문은 반복 중인 반복문을 강제로 멈춰줍니다. 다음 반복으로 스킵해주는 continue문과 구분해서 사용하도록 합니다.

네이버 지도 데이터수집 코드(2019년 12월까지 활용 가능)

# 셀레니움 연습하기
from selenium import webdriver
import time
#1. 웹드라이버 켜기
driver = webdriver.Chrome("./chromedriver")
#2. 네이버 지도 접속하기
driver.get("https://v4.map.naver.com/")
# !!)네이버 지도 업데이트로 추
driver.find_elements_by_css_selector("button.btn_close")[1].click()
#3. 검색창에 검색어 입력하기 // 검색창: input#search-input
search_box = driver.find_element_by_css_selector("input#search-input")
search_box.send_keys("오골계")
#4. 검색버튼 누르기 // 검색버튼: button.spm
search_button = driver.find_element_by_css_selector("button.spm")
search_button.click()
#5. 검색 결과 확인하기
for n in range(1, 20):
# 지연시간주기
time.sleep(1)
# 컨테이너 dl.lsnx_det
# stores = html.select("dl.lsnx_det")
stores = driver.find_elements_by_css_selector("dl.lsnx_det")
for s in stores:
name = s.find_element_by_css_selector("dt > a").text
addr = s.find_element_by_css_selector("dd.addr").text
try:
tel = s.find_element_by_css_selector("dd.tel").text
except:
tel = "전화번호 없음"
# 가게 이름 dt > a
# 가게 주소 dd.addr
# 전화번호 dd.tel
print(name)
print(addr)
print(tel)
# 페이지버튼 div.paginate > *
page_bar = driver.find_elements_by_css_selector("div.paginate > *")
try:
if n%5 != 0:
page_bar[n%5+1].click()
else:
page_bar[6].click()
except:
print("수집완료")
break