본문 바로가기
인공지능/LLM 서비스

셀레니움 - 요기요 리뷰 크롤링

by hyunji00pj 2025. 1. 12.

셀레니움

Selenium은 웹 애플리케이션을 자동화하기 위한 오픈 소스 툴로, 브라우저를 프로그래밍적으로 제어하여 사람처럼 웹을 탐색하거나 상호작용할 수 있게 합니다. 주로 웹 테스트 자동화와 크롤링에 사용되며, 동적인 콘텐츠나 자바스크립트 렌더링이 필요한 웹 페이지에서도 효과적으로 동작합니다. Selenium WebDriver를 사용하면 Python, Java, C# 등 다양한 프로그래밍 언어로 브라우저를 제어할 수 있으며, Chrome, Firefox, Edge 등 여러 브라우저에서 작업이 가능합니다. 이를 통해 로그인, 폼 제출, 버튼 클릭, 데이터 스크랩과 같은 작업을 자동화할 수 있습니다.

 

라이브러리 설치

! pip install selenium

Selenium: 동적 웹 페이지 탐색과 상호작용을 위해 사용

pip install beautifulsoup4

BeautifulSoup (bs4): HTML 문서를 파싱하여 데이터 추출.

추가적으로 사용된 라이브러리

 

#조건이 충족될 떄 까지 대기
from selenium.webdriver.support.ui import WebDriverWait

#특정 조건을 기다릴 때 사용
from selenium.webdriver.support import expected_conditions as EC

from selenium.webdriver.common.by import By
#예외 처리 할것 import
from selenium.common.exceptions import NoSuchElementException, TimeoutException, ElementNotInteractableException
  • time: 딜레이를 추가하여 안정적인 크롤링을 지원.
  • WebDriverWait & expected_conditions: Selenium의 대기 기능으로, 특정 요소가 로드되거나 클릭 가능할 때까지 기다림.
  • By: HTML 요소를 검색할 때 사용.
  • 예외 처리: NoSuchElementException, TimeoutException, ElementNotInteractableException 등.

셀레니움을 이용한 크롤링 코드

1. 크롤링할 URL 설정

BASE_URL = 'https://www.yogiyo.co.kr/mobile/#/'
URL = [
    'https://www.yogiyo.co.kr/mobile/#/546259/',
    'https://www.yogiyo.co.kr/mobile/#/1289963/',
    'https://www.yogiyo.co.kr/mobile/#/1211076/'
]

 

 

  • BASE_URL: 요기요 메인 페이지 URL.
  • URL: 크롤링할 가게의 상세 페이지 URL 리스트. 예를 들어, 각 URL은 특정 가게를 나타냄.
  • 이 리스트에 원하는 가게 URL을 추가하여 크롤링할 대상을 확장할 수 있습니다.

2. 웹 드라이버 설정

chrome_options = webdriver.ChromeOptions()
prefs = {'profile.default_content_setting_values.geolocation': 2}
chrome_options.add_experimental_option('prefs', prefs)

driver = webdriver.Chrome(options=chrome_options)

 

 

  • webdriver.ChromeOptions: 크롬 드라이버의 설정을 정의합니다.
  • prefs: 위치 권한을 비활성화(geolocation: 2)하여 위치 허용 팝업을 방지.
  • webdriver.Chrome(options=chrome_options): 크롬 드라이버 객체 생성.

3. 지역 검색

driver.get(BASE_URL)
time.sleep(2)

search_box = WebDriverWait(driver, 5).until(
    EC.visibility_of_element_located((By.XPATH, '//*[@id="search"]/div/form/input')))
search_box.send_keys('역삼동')

search_btn = WebDriverWait(driver, 5).until(
    EC.visibility_of_element_located((By.XPATH, '//*[@id="button_search_address"]/button[2]')))
search_btn.click()
time.sleep(2)

first_address = WebDriverWait(driver, 5).until(
    EC.visibility_of_element_located((By.XPATH, '//*[@id="search"]/div/form/ul/li[3]/a')))
first_address.click()
time.sleep(2)

 

 

  • BASE_URL로 이동 후 역삼동 지역을 검색합니다.
  • 검색 결과에서 첫 번째 주소를 선택하여 해당 지역의 요기요 데이터를 불러옵니다.
  • WebDriverWait: 특정 요소가 나타날 때까지 대기합니다.
  • time.sleep: 로딩 시간을 보장.

4. 가게 상세 페이지로 이동

driver.get(url)
time.sleep(2)

 

  • 입력된 URL(url)로 이동하여 가게의 상세 페이지를 로드합니다.

5. 클린 리뷰 탭 클릭

clean_review_btn = driver.find_element(By.XPATH, '//*[@id="content"]/div[2]/div[1]/ul/li[2]/a')
clean_review_btn.click()
  • 클린 리뷰 탭의 버튼을 클릭하여 클린 리뷰를 불러옵니다.

6. 모든 리뷰 로드

while True:
    try:
        more_btn = WebDriverWait(driver, 3).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="review"]/li/a')))
        more_btn.click()
        driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
        time.sleep(1)
    except (NoSuchElementException, TimeoutException, ElementNotInteractableException):
        break

 

  • "더보기" 버튼이 존재할 때까지 클릭하여 모든 리뷰를 불러옵니다.
  • driver.execute_script: 화면을 아래로 스크롤.
  • 더 이상 버튼이 없으면 반복문을 종료합니다.

7. HTML 파싱 및 데이터 추출

soup = BeautifulSoup(driver.page_source, 'html.parser')

restaurant_name = soup.select_one(
    '#content > div.restaurant-detail.row.ng-scope > div.col-sm-8 > div.restaurant-info > div.restaurant-title > span'
).get_text(strip=True)

reviews = soup.select('#review > li')
review_texts = [review.get_text(strip=True) for review in reviews][1:]

menus = soup.select('#review > li > div.order-items.default.ng-binding')
all_menu = [menu.get_text(strip=True) for menu in menus]

 

 

  • soup.select_one: 가게 이름을 추출.
  • soup.select: 리뷰(#review > li)와 메뉴 정보를 추출.
  • get_text(strip=True): 텍스트만 추출하며, 양 끝 공백 제거.

8. 데이터 구조화

document = {
    "restaurant": restaurant_name,
    "url": url,
    "reviews": []
}
for menu, text in zip(all_menu, review_texts):
    document['reviews'].append({
        "menus": menu,
        "review_text": text
    })

 

 

  • restaurant_name, url, reviews 데이터를 구조화하여 저장.
  • 메뉴와 리뷰를 한 쌍으로 묶어 document['reviews']에 저장.

9. 출력 예시

{'restaurant': '삼첩분식-논현점', 'url': 'https://www.yogiyo.co.kr/mobile/#/1357527/',
'reviews': 
[{'menus': '[삼첩] 떡볶이/1(사이즈 선택(1~2인),
맵기 선택(쓰읍 (1단계)),떡 선택(떡/어묵 반반),감자 선택((시그니처) 감자폭탄 X)),
누드순대/1,어묵탕/1,순대튀김 6개/1,김말이튀김 1개/1',
'review_text': 
'pr**님2일 전신고★★★★★맛★5양★5배달★0[삼첩] 떡볶이/1(사이즈 선택(1~2인),
맵기 선택(쓰읍 (1단계)),떡 선택(떡/어묵 반반),감자 선택((시그니처) 감자폭탄 X)),
누드순대/1,어묵탕/1,순대튀김 6개/1,김말이튀김 1개/1맛있어요!\n감사합니다사장님15시간 전신고pr**님,
감사합니다! 주문해 주셔서 감사드려요. 맛있게 드실 수 있어서 감사합니다. 
다양한 메뉴를 시킴으로써 다양한 맛을 즐기셨군요. 저희 매장의 대표 메뉴를 주문해주셔서 감사합니다. 
항상 더 나은 맛과 서비스로 보답하겠습니다. 앞으로도 많은 이용 부탁 드려요! 🌟🍲🍢'}, 
{'menus': '일첩 세트/1(떡볶이 선택(삼첩 떡볶이),맵기 선택(바질크림은 적용 불가합니다)
(쓰으읍 (2단계)),떡 선택 (마라로제, 바질은 적용 불가합니다)(떡/어묵 반반),
기본 구성(튀김어묵 4 + 당면만두 4),사이드 선택([꼬마] 새우마요김밥 2줄),
더하다 추가선택(닭껍질튀김))', 
'review_text': 'wj**님2024년 11월 17일신고★★★★★맛★5양★5배달★0일첩 세트/1(떡볶이 선택(삼첩 떡볶이),
맵기 선택(바질크림은 적용 불가합니다)(쓰으읍 (2단계)),
떡 선택 (마라로제, 바질은 적용 불가합니다)(떡/어묵 반반),
기본 구성(튀김어묵 4 + 당면만두 4),사이드 선택([꼬마] 새우마요김밥 2줄),
더하다 추가선택(닭껍질튀김)).사장님2024년 11월 19일신고wj**님, 이용해주셔서 감사합니다. 
세트 메뉴를 주문해주셔서 감사드리며, 
즐거운 식사시간이었길 바랍니다. 맛있는 음식과 함께 즐거운 서비스를 제공할 수 있어 감사합니다. 
리뷰를 남겨주셔서 진심으로 감사드리며, 
다음에도 맛있는 음식으로 모시기를 기대하겠습니다. 🌟🙏🍜🍤'}

 

요기요 리뷰 크롤링.mp4
2.76MB

 

'인공지능 > LLM 서비스' 카테고리의 다른 글

Gradio  (0) 2025.01.12
프롬프트 엔지니어링  (0) 2025.01.12
생성형 AI에 대해서 - GPT API  (0) 2025.01.12
예스 24, 교보문고 크롤링(mongoDB,Excel 저장)  (0) 2025.01.12
크롤링  (0) 2025.01.12