본문 바로가기
인공지능/데이터분석

커피 프랜차이즈의 입점전략 - 스타벅스 정보 크롤링

by hyunji00pj 2025. 1. 24.

 

스타벅스 "커피가 아닌 '공간'을 판매한다"

https://kid.chosun.com/site/data/html_dir/2022/06/02/2022060202963.html

 

프랜차이즈 브랜드 立地<입지> 선택의 법칙… 스타벅스 옆엔 이디야가 있다?

 

kid.chosun.com

이디야·올리브영 "스벅 옆자리가 좋아"

커피 전문점 이디야커피는 한때 '스타벅스 옆자리를 사수하라'는 전략을 구사했어요. 점심시간과 같은 '피크 타임' 때 커피를 마시러 온 사람들이 자리가 없으면 자연스레 바로 옆 이디야 매장을 찾도록 한 겁니다. 저렴한 커피 가격으로 차별성도 강조했죠. 실제로 스타벅스 옆 매장의 매출은 다른 매장보다 높았다고 해요.

건강·미용 전문 매장인 CJ 올리브영도 스타벅스 커피점 인근에 위치한 모습을 쉽게 볼 수 있어요. 20~30대(代)가 주(主) 고객층이라는 점에서 서로 겹치죠.

 

위 기사 내용에 따르면 이디야와 올리브영은 스타벅스와 가까운 자리에 점포를 마련하여 매출을 높이는 전략을 구사한다고 합니다.

 

이번 포스팅에서는 그 입점 전략을 데이터를 분석해 확인해 보기 위해 먼저 스타벅스의 정보를 크롤링 하겠습니다.

 

 

스타벅스 정보 크롤링

필요한 모듈 설치 및 import

!pip install pandas
import time
import re
import pandas as pd
from selenium import webdriver
from selenium.webdriver import ActionChains # ActionChains: 마우스나 키보드와 같은 복잡한 사용자 상호작용을 시뮬레이션 하는데 사용 ex 드래그,드롭 등등
from selenium.webdriver.common.by import By # xpath string같은거 상수화 시키는 모듈
from selenium.webdriver.support.ui import WebDriverWait #실제 데이터 받을때 까지 기다림
from selenium.webdriver.support import expected_conditions as EC # 데이터 대기시켜서 불러오는거
from bs4 import BeautifulSoup

 

먼저 셀레니움을 사용하여 스타벅스 웹사이트에서 서울 지역 매장의 이름, 주소 위,경도를 스크래핑하여 데이터프레임으로 반환하는 함수를 정의하겠습니다.

def fetch_starbucks():
    starbucks_url = 'https://www.starbucks.co.kr/index.do'
    driver = webdriver.Chrome()
    driver.maximize_window()
    driver.get(starbucks_url)
    time.sleep(1)

    action = ActionChains(driver)
    first_tag = driver.find_element(By.CSS_SELECTOR,'#gnb > div > nav > div > ul > li.gnb_nav03')
    second_tag = driver.find_element(By.CSS_SELECTOR,'#gnb > div > nav > div > ul > li.gnb_nav03 > div > div > div > ul:nth-child(1) > li:nth-child(3) > a')

    # perform() : ActionChains를 실행
    action.move_to_element(first_tag).move_to_element(second_tag).click().perform()

    seoul_tag = WebDriverWait(driver,10).until(
        EC.element_to_be_clickable(
            (By.CSS_SELECTOR,'#container > div > form > fieldset > div > section > article.find_store_cont > article > article:nth-child(4) > div.loca_step1 > div.loca_step1_cont > ul > li:nth-child(1) > a')
        ))
    seoul_tag.click()

    # 매장 정보 담기 위한 빈 리스트
    store_list = []
    addr_list = []
    lat_list = []
    lng_list = []


    WebDriverWait(driver,5).until(EC.presence_of_all_elements_located((By.CLASS_NAME,'set_gugun_cd_btn')))
    gu_elements = driver.find_elements(By.CLASS_NAME,'set_gugun_cd_btn')
    WebDriverWait(driver,5).until(
        EC.element_to_be_clickable(
            (By.CSS_SELECTOR,'#mCSB_2_container > ul > li:nth-child(1) > a')
        ))
    gu_elements[0].click()

    # 매장정보가 뜨기까지 대기
    WebDriverWait(driver, 5).until(EC.presence_of_all_elements_located((By.CLASS_NAME, 'quickResultLstCon')))

    # 뷰티풀숲을 이용해 스크래핑 하기
    req = driver.page_source
    soup = BeautifulSoup(req, 'html.parser')
    stores = soup.find('ul', 'quickSearchResultBoxSidoGugun').find_all('li')

    for store in stores:
        store_name = store.find('strong').text
        store_addr = store.find('p').text
        store_addr = re.sub(r'\d{4}-\d{4}$','',store_addr).strip()
        store_lat = store['data-lat'] #속성으로 찾는거
        store_lng = store['data-long'] #속성으로 찾는거
        store_list.append(store_name)
        addr_list.append(store_addr)
        lat_list.append(store_lat)
        lng_list.append(store_lng)

    df = pd.DataFrame({
        'store': store_list,
        'addr': addr_list,
        'lat': lat_list,
        'lng': lng_list
    })
    
    driver.quit()
    return df

코드 설명

 

Selenium 웹 드라이버 초기화

    starbucks_url = 'https://www.starbucks.co.kr/index.do'
    driver = webdriver.Chrome()
    driver.maximize_window()
    driver.get(starbucks_url)
    time.sleep(1)
  • starbucks_url: 스타벅스 코리아 웹사이트의 메인 URL.
  • webdriver.Chrome(): Chrome 웹 드라이버를 사용하여 브라우저를 실행.
  • driver.maximize_window(): 브라우저 창을 최대화.
  • driver.get(): 브라우저를 열고 스타벅스 웹사이트로 이동.
  • time.sleep(1): 페이지 로딩을 기다리기 위해 1초 대기.

메뉴 이동 (ActionChains 사용)

    action = ActionChains(driver)
    first_tag = driver.find_element(By.CSS_SELECTOR,'#gnb > div > nav > div > ul > li.gnb_nav03')
    second_tag = driver.find_element(By.CSS_SELECTOR,'#gnb > div > nav > div > ul > li.gnb_nav03 > div > div > div > ul:nth-child(1) > li:nth-child(3) > a')
    action.move_to_element(first_tag).move_to_element(second_tag).click().perform()

 

  • ActionChains: Selenium에서 마우스 동작을 시뮬레이션하는 데 사용됩니다.
    • move_to_element(): 특정 HTML 요소 위로 마우스를 이동.
    • click(): 마우스 클릭 동작을 실행.
    • perform(): 지정한 동작을 실행.
  • CSS Selector: 메뉴에서 "매장 찾기"로 이동하기 위해 사용된 CSS 선택자입니다.
    1. first_tag: 상단 네비게이션 메뉴에서 "매장 찾기" 섹션에 해당.
    2. second_tag: "매장 찾기" 하위 메뉴로 이동하여 클릭.

서울 지역 클릭

    seoul_tag = WebDriverWait(driver,10).until(
        EC.element_to_be_clickable(
            (By.CSS_SELECTOR,'#container > div > form > fieldset > div > section > article.find_store_cont > article > article:nth-child(4) > div.loca_step1 > div.loca_step1_cont > ul > li:nth-child(1) > a')
        ))
    seoul_tag.click()

 

 

  • WebDriverWait: 특정 조건이 만족될 때까지 대기.
    • EC.element_to_be_clickable: 요소가 클릭 가능한 상태가 될 때까지 기다림.
  • 서울 클릭: 매장 검색에서 서울 지역을 선택.

데이터 수집 준비

    store_list = []
    addr_list = []
    lat_list = []
    lng_list = []

 

  • 각 정보를 저장할 리스트 초기화:
    • store_list: 매장 이름.
    • addr_list: 매장 주소.
    • lat_list: 위도.
    • lng_list: 경도.

구 선택

    WebDriverWait(driver,5).until(EC.presence_of_all_elements_located((By.CLASS_NAME,'set_gugun_cd_btn')))
    gu_elements = driver.find_elements(By.CLASS_NAME,'set_gugun_cd_btn')
    gu_elements[0].click()

 

  • find_elements: "구 선택" 버튼 목록을 가져옵니다.
  • 구 클릭: 첫 번째 구(예: 강남구)를 선택.

매장 정보 로딩

    WebDriverWait(driver, 5).until(EC.presence_of_all_elements_located((By.CLASS_NAME, 'quickResultLstCon')))

 

EC.presence_of_all_elements_located: 매장 정보가 모두 로드될 때까지 대기.

 

BeautifulSoup로 매장 정보 파싱

    req = driver.page_source
    soup = BeautifulSoup(req, 'html.parser')
    stores = soup.find('ul', 'quickSearchResultBoxSidoGugun').find_all('li')

 

  • driver.page_source: 현재 페이지의 HTML 소스 코드를 가져옵니다.
  • BeautifulSoup 파싱: HTML을 분석하고 매장 정보를 추출.
    • find('ul', 'quickSearchResultBoxSidoGugun'): 매장 목록을 포함한 <ul> 태그를 찾음.
    • find_all('li'): 매장 정보를 포함하는 각 <li> 태그를 가져옴.

매장 정보 추출

    for store in stores:
        store_name = store.find('strong').text
        store_addr = store.find('p').text
        store_addr = re.sub(r'\d{4}-\d{4}$','',store_addr).strip()
        store_lat = store['data-lat']
        store_lng = store['data-long']
        store_list.append(store_name)
        addr_list.append(store_addr)
        lat_list.append(store_lat)
        lng_list.append(store_lng)

 

 

  • 매장 정보 추출:
    • store_name: 매장 이름 (<strong> 태그에서 추출).
    • store_addr: 매장 주소 (<p> 태그에서 추출). 주소에서 전화번호 형식(0000-0000) 제거.
    • store_lat, store_lng: 매장의 위도와 경도(data-lat, data-long 속성에서 추출).
  • 추출한 데이터를 각각의 리스트에 저장.

DataFrame 생성

    df = pd.DataFrame({
        'store': store_list,
        'addr': addr_list,
        'lat': lat_list,
        'lng': lng_list
    })

 

pandas.DataFrame 생성:

  • store: 매장 이름.
  • addr: 매장 주소.
  • lat: 위도.
  • lng: 경도.

브라우저 종료 및 결과 반환

    driver.quit()
    return df

 

  • driver.quit(): 웹 드라이버 종료.
  • return df: 수집한 데이터를 DataFrame으로 반환.

 

starbucks_df.to_csv('starbucks_seoul.csv', index=False, encoding='utf-8-sig')

 수집한 데이터를 csv파일로 저장

 

다음 포스팅에 이어서 크롤링으로 수집한 데이터를 이용해 커피 프랜차이즈들이 스타벅스와 가까운 자리에 점포를 마련하는지 확인해 보도록 하겠습니다