rubus0304 님의 블로그
[프로젝트 5일차] 본문
대전 명소 웹크롤링 완성
[에러코드 - 상세정보부터]
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
from selenium.webdriver.common.action_chains import ActionChains
import json
import time
from time import sleep
import random
import re
# 크롤링 데이터 저장용 리스트
store_data = []
# 왼쪽 iframe 전환
def switch_left():
driver.switch_to.parent_frame()
iframe = driver.find_element(By.XPATH, '//*[@id="searchIframe"]')
driver.switch_to.frame(iframe)
# 오른쪽 iframe 전환
def switch_right():
driver.switch_to.parent_frame()
iframe = driver.find_element(By.XPATH, '//*[@id="entryIframe"]')
driver.switch_to.frame(iframe)
options = webdriver.ChromeOptions()
options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3')
options.add_argument('window-size=1380,900')
driver = webdriver.Chrome(options=options)
# 네이버 지도 URL
# 지도에 검색한 키워드 URL을 아래 복붙
driver.get(url=URL)
driver.implicitly_wait(5)
# 한 번에 끝까지 스크롤
def scroll_to_bottom():
last_height = driver.execute_script("return document.body.scrollHeight") # 초기 높이 가져오기
while True:
# 페이지 끝까지 스크롤
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
sleep(2) # 로딩 대기
new_height = driver.execute_script("return document.body.scrollHeight") # 새로운 높이 가져오기
if new_height == last_height: # 높이가 더 이상 변하지 않으면 종료
break
last_height = new_height
# 1000px씩 끊어서 스크롤
def scroll_to_1000px(scrollable_element):
last_height = driver.execute_script("return arguments[0].scrollHeight", scrollable_element) # 초기 높이 가져오기
while True:
# 요소 내에서 아래로 1000px 스크롤
driver.execute_script("arguments[0].scrollTop += 1000;", scrollable_element)
sleep(2) # 로딩 대기
new_height = driver.execute_script("return arguments[0].scrollHeight", scrollable_element) # 새로운 높이 가져오기
if new_height == last_height: # 높이가 더 이상 변하지 않으면 종료
break
last_height = new_height
# 누적 크롤링 시간 확인용 변수
total_elapsed_time = 0
#####중간에 멈췄을 경우 아래 코드 실행#####
# switch_left()
# # 페이지 이동 필요만큼 아래 두 행 복붙
# driver.find_element(By.XPATH, '//*[@id="app-root"]/div/div[2]/div[2]/a[last()]').click()
# sleep(1)
# driver.find_element(By.XPATH, '//*[@id="app-root"]/div/div[2]/div[2]/a[last()]').click()
# sleep(1)
# driver.find_element(By.XPATH, '//*[@id="app-root"]/div/div[2]/div[2]/a[last()]').click()
# sleep(1)
# driver.find_element(By.XPATH, '//*[@id="app-root"]/div/div[2]/div[2]/a[last()]').click()
# sleep(1)
while True:
switch_left()
# 맨 밑까지 스크롤 (1000px 씩 끊어서)
scrollable_element = driver.find_element(By.CLASS_NAME, "Ryr1F")
scroll_to_1000px(scrollable_element)
# 현재 페이지 번호
page_no = driver.find_element(By.XPATH, '//a[contains(@class, "mBN2s qxokY")]').text
# n번째 가게부터 시작
#####처음 실행할 경우#####
elements = driver.find_elements(By.XPATH, '//*[@id="_pcmap_list_scroll_container"]//li')
# # #####중간에 멈춰서 다시 실행할 경우 아래 코드의 숫자 수정 필요#####
# # elements = driver.find_elements(By.XPATH, '//*[@id="_pcmap_list_scroll_container"]//li')[7:] if page_no == '5' else driver.find_elements(By.XPATH, '//*[@id="_pcmap_list_scroll_container"]//li')
for index, e in enumerate(elements, start=1):
# 광고 태그
is_ad = False
# 광고면 is_ad = True 아니면 기존값 False
try:
ad_tag = e.find_element(By.XPATH, './/span[@class="place_blind" and text()="광고"]')
if ad_tag:
is_ad = True
except:
pass
# 데이터 초기화
store_info = {
'index' : index,
'page' : page_no,
'store_name' : '',
'category' : '',
# 'is_ad' : is_ad,
# 'rating' : 0.0,
'visited_review' : 0,
'blog_review' : 0,
'address' : '',
'link' : '',
'phone_num' : '',
'menu' : [],
'keyword_reviews' : [],
}
# 크롤링 시간 확인용
start_time = time.time()
# 가게 클릭 및 상세 페이지 이동
clickable_element = e.find_element(By.CLASS_NAME, 'ouxiq').find_element(By.XPATH, ".//a/div/div/span[1]")
# if is_ad == True:
# # 요소의 위치 가져오기
# location = clickable_element.location
# size = clickable_element.size
# # 광고 태그 크기 확인
# ad_tag_width = ad_tag.size['width']
# # 약간 오른쪽으로 위치 조정 (x_offset 값 수정 가능)
# x_offset = ad_tag_width + 5 # 가장 왼쪽에서 광고 태그만큼 5 px 이동
# y_offset = size['height'] // 2 # 요소의 세로 중심
# # ActionChains로 클릭 위치 이동
# actions = ActionChains(driver)
# actions.move_to_element_with_offset(clickable_element, x_offset, y_offset).click().perform()
# else:
clickable_element.click()
sleep(2)
switch_right()
try:
# 상세 정보 크롤링
try:
scroll_to_bottom()
sleep(2)
title = driver.find_element(By.XPATH, '//div[@class="zD5Nm undefined"]')
store_info['store_name'] = title.find_element(By.XPATH, './/div[1]/div[1]/span[1]').text
store_info['category'] = title.find_element(By.XPATH, './/div[1]/div[1]/span[2]').text
except:
pass
# 리뷰 정보
try:
review_elements = title.find_elements(By.XPATH, './/div[2]/span')
if len(review_elements) > 2:
store_info['rating'] = review_elements[0].text
store_info['visited_review'] = review_elements[1].text
store_info['blog_review'] = review_elements[2].text
elif len(review_elements) > 1:
store_info['visited_review'] = review_elements[0].text
store_info['blog_review'] = review_elements[1].text
except:
pass
# 가게 주소 및 영업 시간
try:
store_info['address'] = driver.find_element(By.XPATH, '//span[@class="LDgIH"]').text
except:
pass
try:
store_info['phone_num'] = driver.find_element(By.XPATH, '//span[@class="xlx7Q"]').text
except:
pass
# 링크
try:
store_info['link'] = driver.find_element(By.XPATH, '//div[@class="jO09N"]/a').text
except:
pass
# 호텔 최저가
try:
menu_elements = driver.find_elements(By.XPATH, '//ul[contains(@class, "jG53P")]/li')
store_info['menu'] = []
for menu_element in menu_elements:
name = menu_element.find_element(By.XPATH, './/div[@class="FtVpy"]').text
price = menu_element.find_elements(By.XPATH, './/span[@class="QW7xr"]')
price_texts = [p.text for p in price]
info = menu_element.find_elements(By.XPATH, './/div[contains(@class, "uNeKV")]')
info_texts = [i.text for i in info]
store_info['menu'].append({"name": name,"price": price_texts, 'info':info_texts})
except:
pass
# 방문자 리뷰
try:
driver.find_element(By.XPATH, '//div[@class="KERaF"]').click()
sleep(2)
parent_element_review = driver.find_element(By.XPATH, '//div[@class="wvfSn"]')
child_elements_review = parent_element_review.find_elements(By.XPATH, './div[@class="jypaX" or @class="mrSZf"]')
store_info['keyword_reviews'] = [child.text for child in child_elements_review]
except:
pass
except Exception as ex:
print('------------ 데이터 크롤링 오류 ------------' )
print(ex)
store_data.append(store_info)
switch_left()
sleep(2)
# JSON 형식으로 저장
# #######!!!!!!!!n번째 실행할 경우, 아래 cafe_data 뒤에 넘버링 필수!!!!!!!!#######
with open("tourist_data.json", "w", encoding="utf-8") as f:
json.dump(store_data, f, ensure_ascii=False, indent=4)
# 출력 (테스트용)
print(json.dumps(store_info, ensure_ascii=False, indent=4))
elapsed_time = time.time() - start_time # 소요 시간 계산
total_elapsed_time += elapsed_time # 누적 시간 갱신
print(f"Crawling Time: {elapsed_time:.2f} seconds") # 소요 시간 출력
print(f"Total Elapsed Time: {total_elapsed_time:.2f} seconds") # 누적 시간 출력
# 다음 페이지로 이동
next_page = driver.find_element(By.XPATH, '//*[@id="app-root"]/div/div[2]/div[2]/a[last()]').get_attribute('aria-disabled')
if next_page == 'false':
driver.find_element(By.XPATH, '//*[@id="app-root"]/div/div[2]/div[2]/a[last()]').click()
else:
break
'Data Analyst > daily' 카테고리의 다른 글
[프로젝트 6일차] (0) | 2025.01.14 |
---|---|
[프로젝트 4일차] (0) | 2025.01.10 |
[QCC 6회차] (0) | 2025.01.10 |
[프로젝트 2일차] (0) | 2025.01.08 |
[최종프로젝트 1일차] (0) | 2025.01.07 |