Selenium 활용하여 네이버 지도 크롤링

toto9602·2021년 12월 4일
1

이 포스팅은, 필자가 올해 5월에 진행했던 해커톤에서
데이터 크롤링을 담당하며 작성한 코드를 정리한 것이다!

소스 코드의 실행 결과로 생성된 csv 파일을 db에 저장하기만 하고,
따로 소스 코드를 github에 올려두거나 하지 않아,
정리하는 목적으로 적어두기!!

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC #selenium에서 사용할 모듈 import

import time
import requests
from bs4 import BeautifulSoup
import re
import csv
driver = webdriver.Chrome("./chromedriver") #selenium 사용에 필요한 chromedriver.exe 파일 경로 지정

driver.get("https://map.naver.com/v5/") #네이버 신 지도 
try:
   element = WebDriverWait(driver, 10).until(
       EC.presence_of_element_located((By.CLASS_NAME, "input_search"))
   ) #입력창이 뜰 때까지 대기
finally:
   pass

search_box = driver.find_element_by_class_name("input_search")
search_box.send_keys("서울 칵테일바")
search_box.send_keys(Keys.ENTER) #검색창에 "서울 칵테일바" 입력

time.sleep(7) #화면 표시 기다리기
frame = driver.find_element_by_css_selector("iframe#searchIframe")

driver.switch_to.frame(frame)

time.sleep(3)
# 여기까지 iframe 전환

scroll_div = driver.find_element_by_xpath("/html/body/div[3]/div/div[2]/div[1]")
#검색 결과로 나타나는 scroll-bar 포함한 div 잡고
driver.execute_script("arguments[0].scrollBy(0,2000)", scroll_div)
time.sleep(2)
driver.execute_script("arguments[0].scrollBy(0,2000);", scroll_div)
time.sleep(2)
driver.execute_script("arguments[0].scrollBy(0,2000);", scroll_div)
time.sleep(2)
driver.execute_script("arguments[0].scrollBy(0,2000);", scroll_div)
time.sleep(2)
driver.execute_script("arguments[0].scrollBy(0,2000);", scroll_div)
time.sleep(2)
#여기까지 scroll
#맨 아래까지 내려서 해당 페이지의 내용이 다 표시되게 함

# csv 파일 생성
file = open('stores.csv', mode='w', newline='')
writer = csv.writer(file)
writer.writerow(["place", "rate", "address", "info", "image"])
final_result = []
time.sleep(1)
# # 반복 시작

i = 2
while i<=5: #몇 페이지까지 크롤링할 것인지 지정
   stores_box = driver.find_element_by_xpath("/html/body/div[3]/div/div[2]/div[1]/ul")
   stores = driver.find_elements_by_css_selector("li._3t81n._1l5Ut")
   #해당 페이지에서 표시된 모든 가게 정보
   
   for store in stores: #한 페이지 내에서의 반복문. 순차적으로 가게 정보에 접근
       name = store.find_element_by_css_selector("span._3Yilt").text #가게 이름
       try:    
           rating = re.search('/span>(\d).', store.find_element_by_css_selector("span._3Yzhl._1ahw0").get_attribute('innerHTML')).groups()[0]
       except:
           rating = ''
       time.sleep(3)
       # 평점 숫자 부분만 rating에 담음. 평점이 없는 경우가 있어 예외 처리
       try:
           img_src = re.search('url[(]"([\S]+)"', store.find_element_by_css_selector("div.cb7hz.undefined").get_attribute('style')).groups()[0]
       except:
           img_src = ''
          #역시 대표 이미지가 없는 경우가 있어 예외 처리
       click_name = store.find_element_by_css_selector("span._3Yilt")
       click_name.click() 
       # 가게 주소, 홈페이지 링크를 확인하려면 가게 이름을 클릭해 세부 정보를 띄워야 함.


       driver.switch_to.default_content()
       time.sleep(7)        
       ##오래 헤맸던 부분!! switch_to.default_content()로 전환해야 frame_in iframe을 제대로 잡을 수 있다. 
       
       frame_in = driver.find_element_by_xpath('/html/body/app/layout/div[3]/div[2]/shrinkable-layout/div/app-base/search-layout/div[2]/entry-layout/entry-place-bridge/div/nm-external-frame-bridge/nm-iframe/iframe')

       driver.switch_to.frame(frame_in) 
       # 가게 이름을 클릭하면 나오는 세부 정보 iframe으로 이동
       time.sleep(3)
       try:
           address = re.search('서울\s(\w+)\s', driver.find_element_by_css_selector("span._2yqUQ").text).groups()[0]
       except:
           address = ''
          #주소 정보 확인
       try:
           link_url = driver.find_element_by_css_selector("a._1RUzg").text
       except:
           link_url = ''
          # 홈페이지 url 확인
       store_info = {
           'placetitle':name,
           'rate':rating,
           'address':address,
           'info':link_url,
           'image':img_src
       }
       #크롤링한 정보들을 store_info에 담고
       print(name, rating, address, img_src, link_url)
       print("*" * 50)
       final_result.append(store_info)
       # 출력해서 확인 후 final_result에 저장

       driver.switch_to.default_content()
       driver.switch_to.frame(frame)
       time.sleep(8)
       # 한 페이지 크롤링 끝
     
     # '2'페이지로 이동하는 버튼 클릭 후 i 1증가 
   next_button = driver.find_element_by_link_text(str(i))
   next_button.click()
   i = i+1
   time.sleep(8)
   
#while문이 종료되면 크롤링 종료

for result in final_result: #크롤링한 가게 정보에 순차적으로 접근 & csv 파일 작성
    row = []
    row.append(result['placetitle'])
    row.append(result['rate'])
    row.append(result['address'])
    row.append(result['info'])
    row.append(result['image'])
    writer.writerow(row)
    
print(final_result)
#최종 결과 확인

주석으로 잠시 언급했지만,

iframe에서 다른 iframe으로 전환할 때,

driver.switch_to.default_content()

해당 코드를 통해 먼저 기본 위치로 되돌려주어야 다른 iframe을 제대로 찾을 수 있다는 것을 몰라
초조한 마음으로 몇 시간을 구글링했던 기억이 난다.

정해진 시간 안에 크롤링을 성공하지 못하면
서비스를 완성할 수 없었던 상황이라, 코드 작성 당시에는 마감 시각 전에 에러 없이 크롤링을 끝낸 것만으로 다행이었지만...

화면에 엘리먼트가 표시되지 않아 엘리먼트를 찾지 못해서 에러가 나는 경우를 방지하기 위해
time.sleep()을 과도하게 사용하느라 크롤링에 불필요하게 많은 시간이 걸리는 코드이기도 하다.

selenium을 구글링으로 배워 처음 써 보던 때라 그리 효율적이진 못했지만,
그래도 새로운 것을 시도해 보는 것만은 즐거웠던

첫 번째 해커톤에서의 데이터 크롤링!

profile
주니어 백엔드 개발자입니다! 조용한 시간에 읽고 쓰는 것을 좋아합니다 :)

3개의 댓글

comment-user-thumbnail
2022년 11월 10일

덕분에 도움이 많이 됬습니다!감사합니다^^

1개의 답글
comment-user-thumbnail
2023년 3월 18일

안녕하세요! 덕분에 네이버 지도 정보 크롤링에 도움이 정말 많이 되었습니다.

선생님 따라서 해보다가 오류나는 부분있어서 문의차 댓글을 남기게 되었습니다.
아래는 제가 예시로 변형해서 코드를 수정&제작하였는데요.
데이터를 크롤링하는 것 까지는 잘 되고, 실행도 오류 없이 잘 되는데요.

저장하는 부분에서 데이터를 아무것도 저장하지 않은채 빈 파일만 생성되네요.. 혹시
제가 실수한 부분이 있는지 한 번 봐주실 순 없을까요??

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC #selenium에서 사용할 모듈 import
from webdriver_manager.chrome import ChromeDriverManager
import time
import requests
from bs4 import BeautifulSoup
import re
import csv

브라우저 생성

driver = webdriver.Chrome(ChromeDriverManager().install())
driver.maximize_window()

페이지 접속

URL = "https://map.naver.com/v5/search/%EB%B6%80%EC%82%B0%20%EB%8F%99%EB%9E%98%EA%B5%AC%20%EB%84%A4%EC%9D%BC?c=12,0,0,0,dh"
driver.get(url=URL)
time.sleep(10)

frame = driver.find_element(By.CSS_SELECTOR,"iframe#searchIframe")

driver.switch_to.frame(frame)

time.sleep(3)

여기까지 iframe 전환

scroll_div = driver.find_element(By.CSS_SELECTOR,"#_pcmap_list_scroll_container")
#검색 결과로 나타나는 scroll-bar 포함한 div 잡고
driver.execute_script("arguments[0].scrollBy(0,2000)", scroll_div)
time.sleep(2)
driver.execute_script("arguments[0].scrollBy(0,2000);", scroll_div)
time.sleep(2)
driver.execute_script("arguments[0].scrollBy(0,2000);", scroll_div)
time.sleep(2)
driver.execute_script("arguments[0].scrollBy(0,2000);", scroll_div)
time.sleep(2)
driver.execute_script("arguments[0].scrollBy(0,2000);", scroll_div)
time.sleep(2)
#여기까지 scroll
#맨 아래까지 내려서 해당 페이지의 내용이 다 표시되게 함

csv 파일 생성

file = open('동래구_네일.csv', 'w', newline='', encoding='utf-8-sig')
writer = csv.writer(file)
writer.writerow(["place", "rate", "address", "info", "image"])
final_result = []
time.sleep(1)

# 반복 시작

i = 2
while i<=2: #몇 페이지까지 크롤링할 것인지 지정
stores_box = driver.find_element(By.CSS_SELECTOR,"#app-root > div > div.XUrfU > div.zRM9F > a:nth-child(6) > span")
stores = driver.find_elements(By.CSS_SELECTOR,"#_pcmap_list_scroll_container > ul > li")
#해당 페이지에서 표시된 모든 가게 정보

for store in stores: #한 페이지 내에서의 반복문. 순차적으로 가게 정보에 접근
name = store.find_element(By.CSS_SELECTOR,"#_pcmap_list_scroll_container > ul > li > div.QTjRp > a > div.G9H9r > div > span.place_bluelink.O_Uah").text #가게 이름
try:
rating = store.find_element(By.CSS_SELECTOR,"#_pcmap_list_scroll_container > ul > li > div.QTjRp > a > div.z5QIY > span.piUxu.vYUUc > em").text
except:
rating = ''
time.sleep(3)

   # 평점 숫자 부분만 rating에 담음. 평점이 없는 경우가 있어 예외 처리
   try:
       img_src = store.find_element(By.CSS_SELECTOR,"#_pcmap_list_scroll_container > ul > li > div.QTjRp > a > div.z5QIY > span:nth-child(3)").text
   except:
       img_src = ''
      #역시 대표 이미지가 없는 경우가 있어 예외 처리
   click_name = store.find_element(By.CSS_SELECTOR,"#_pcmap_list_scroll_container > ul > li > div.QTjRp > a > div.G9H9r > div > span.place_bluelink.O_Uah")
   click_name.click() 
   # 가게 주소, 홈페이지 링크를 확인하려면 가게 이름을 클릭해 세부 정보를 띄워야 함.


   driver.switch_to.default_content()
   time.sleep(1)      
   ##오래 헤맸던 부분!! switch_to.default_content()로 전환해야 frame_in iframe을 제대로 잡을 수 있다. 
   frame_in = driver.find_element(By.CSS_SELECTOR,"#entryIframe")

   driver.switch_to.frame(frame_in) 
   # 가게 이름을 클릭하면 나오는 세부 정보 iframe으로 이동
   time.sleep(1)
   
   try:
       address = driver.find_element(By.CSS_SELECTOR,"#app-root > div > div > div > div > div > div.place_section.no_margin.vKA6F > div > div > div.O8qbU.tQY7D > div > a > span.LDgIH").text
   except:
       address = ''
      #주소 정보 확인
   try:
       link_url = driver.find_element(By.CSS_SELECTOR,"#app-root > div > div > div > div > div > div.place_section.no_margin.vKA6F > div > div > div.O8qbU.yIPfO > div > div.jO09N > a").text
   except:
       link_url = ''
      # 홈페이지 url 확인
   store_info = {
       'place':name,
       'rate':rating,
       'address':address,
       'info':link_url,
       'image':img_src
   }
   #크롤링한 정보들을 store_info에 담고
   print(name, rating, address, img_src, link_url)
   print("*" * 50)
   final_result.append(store_info)
   # 출력해서 확인 후 final_result에 저장

   driver.switch_to.default_content()
   driver.switch_to.frame(frame)
   time.sleep(8)
   # 한 페이지 크롤링 끝
 
 # '2'페이지로 이동하는 버튼 클릭 후 i 1증가 

next_button = driver.find_element(By.LINK_TEXT,str(i))
next_button.click()
i = i+1
time.sleep(8)

#while문이 종료되면 크롤링 종료

for result in final_result: #크롤링한 가게 정보에 순차적으로 접근 & csv 파일 작성
row = []
row.append(result['place'])
row.append(result['rate'])
row.append(result['address'])
row.append(result['info'])
row.append(result['image'])
writer.writerow(row)

print(final_result)
#최종 결과 확인

답글 달기