본문 바로가기

Python/크롤링(Crawling)

스타벅스 크롤링하기 + 차트 시각화

*이 글을 읽기전에 작성자 개인의견이 있으니, 다른 블로그와 교차로 읽는것을 권장합니다.*

1. 스타벅스 크롤링

커피전문점 중 스타벅스 브랜드의 매장위치를 크롤링한 후, 여러 차트를 뽑아내어 mongoDB에 데이터를 넣는 토이프로젝트를 해보았다.

모듈설치 

!pip install selenium webdriver-manager
!pip install selenium
!pip install chromedriver_autoinstaller
!pip install folium
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import seaborn as sns
import matplotlib.pyplot as plt
import time
from bs4 import BeautifulSoup
import requests
from matplotlib import rc
import numpy as np
import pandas as pd
import folium # 지도
from folium.features import CustomIcon
import json # json데이터 처리
import warnings # 경고처리
warnings.filterwarnings('ignore')
rc("font", family="Malgun Gothic") # Windows :  
# %matplotlib inline 
get_ipython().run_line_magic("matplotlib", "inline")
# 스타벅스 매장 크롤링
driver=webdriver.Chrome()
driver.get('https://www.starbucks.co.kr/store/store_map.do')

매장이름과 매장주소, 매장좌표를 xpath방식으로 가져오려 했으나, 매장주소는 하위태그를 2번 거쳐 내려가기때문에, find_elements메서드로 가져오는 것이 불가능했다. 이때 select메서드를 활용하면 된다는 생각을 하면 좋았을텐데...

매장이름과 매장좌표만 가져오고 매장주소는 좌표를 RestfulAPI인 KakaoAPI를 이용해서 좌표->주소로 바꾸는 작업으로 리스트를 뽑아냈다.

soup = BeautifulSoup(driver.page_source)
#이름
element1 = driver.find_element('xpath','/html/body/div[4]/div[7]/div/form/fieldset/div/section/article[1]/article/article[2]/div[3]/div[2]/div/div/div[1]/ul')
name_element = element1.find_elements('tag name','li')
name = [li.get_attribute('data-name') for li in name_element]
print(name)

#좌표
element3 = driver.find_element('xpath','/html/body/div[4]/div[7]/div/form/fieldset/div/section/article[1]/article/article[2]/div[3]/div[2]/div/div/div[1]/ul')
lng_element = element3.find_elements('tag name','li')
lat_element = element3.find_elements('tag name', 'li')
lng = [li.get_attribute('data-long') for li in lng_element]
lat = [li.get_attribute('data-lat') for li in lat_element]

# 주소 변환 코드

# 카카오 REST API 키
KAKAO_REST_API_KEY = 'd5efb79bae1afc235f980ea17975b286'  # 여기서 YOUR_KAKAO_REST_API_KEY를 실제 API 키로 바꿔주세요

# 주소 변환 코드
def get_address_from_coordinates(latitude, longitude):
    kakao_endpoint = "https://dapi.kakao.com/v2/local/geo/coord2address.json"
    headers = {
        "Authorization": f"KakaoAK {KAKAO_REST_API_KEY}"
    }
    params = {
        "x": longitude,
        "y": latitude
    }
    response = requests.get(kakao_endpoint, headers=headers, params=params)
    if response.status_code == 200:
        data = response.json()
        if data['documents']:
            address = data['documents'][0]['address']['address_name']
            return address
        else:
            return 'Address not found'
    else:
        return 'Failed to fetch address'

def get_addresses_from_coordinates(coordinates_list):
    addresses = []
    for coordinate in coordinates_list:
        latitude, longitude = coordinate
        address = get_address_from_coordinates(latitude, longitude)
        addresses.append(address)
    return addresses

# 예시 데이터: coordinates 리스트를 사용할 것
# 예시 coordinates 리스트
lng = [li.get_attribute('data-long') for li in lng_element]
lat = [li.get_attribute('data-lat') for li in lat_element]

# 두 리스트를 하나로 묶기
coordinates = [(float(lat[i]), float(lng[i])) for i in range(len(lat))]

# 주소 가져오기
addresses = get_addresses_from_coordinates(coordinates)
for address in addresses:
    print(address)

print("# 지점명, 주소, 좌표가 크롤링")
for i in range(len(name)):
    print(f"지점명: {name[i]} - 주소: {addresses[i]} - 좌표: ({lat[i]}, {lng[i]})")

len(name)

리스트갯수 확인

# 데이터프레임 생성
data = {'지점명': name, '주소': addresses, '위도': lat, '경도': lng}
df = pd.DataFrame(data)

# 데이터프레임 출력
df

612개 모두 확인했다.

df.info()한 결과, 지도위의 마커를 찍어주기 위해서, 위도경도 object type -> float type으로 변경해준다. 여기서부턴 null값 제거와 column이름 등 데이터 전처리작업이다.

df.info()

# null값확인 후 0변환
df.fillna(0, inplace=True)
# 데이터 형식 변환
df['위도'] = df['위도'].astype(float)
df['경도'] = df['경도'].astype(float)
df

icon_image = 'C:\팀과제\starbucks.png'  
data = df
map = folium.Map(location=[data['위도'].mean(), data['경도'].mean()], zoom_start=12)

for index, data_row in data.iterrows():
    popup_str = '{} : {}'.format(
        data_row['지점명'], data_row['주소']
    )
    popup = folium.Popup(popup_str, max_width=600)
    folium.Marker(location=[data_row['위도'], data_row['경도']], popup=popup, icon=CustomIcon(icon_image)).add_to(map)

map

지도위에 스타벅스 마커를 찍어주었다.

이렇게 뽑아낸 DataFrame을 csv파일로 저장한다.

file_path = r'D:\KDT\팀과제\스타벅스_크롤링.csv'  # 파일명.csv에는 저장하려는 파일명을 넣어주세요
df.to_csv(file_path, index=False, encoding='utf-8-sig')

print(f"DataFrame이 {file_path}에 저장되었습니다.")

스타벅스_크롤링.csv
0.04MB

이후 MongoDB에 DataFrame을 넣어주는 작업을 진행한다.

!python -m pip install "pymongo[srv]"==3.11
from pymongo import MongoClient
url = 'mongodb+srv://himdo:몽고DB비번@cluster0.xf3fgoz.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0'
client = MongoClient(url)
print(client)
database = client['starbucks']
collection = database['starbucks_Seoul']
collection
dict= df.to_dict(orient='records')
print(dict)

result = collection.insert_many(dict)

MongoDB 삽입 확인


2. 차트 시각화

지도마커를 확인해본 결과 강남구와 종로구, 중구쪽 스타벅스 마커가 많은 것을 확인했다. 따라서, 구별로 데이터를 뽑아 시각화 자료로 확인해본다.

!pip install pandasecharts
!pip install pyecharts
!pip install pandas
!pip install pyecharts-jupyter-installer
!pyecharts-jupyter-installer install
!pip install selenium
!pip install pyecharts-snapshot
!pip install webdriver-manager
import pandas as pd
import IPython
from pandasecharts import echart
from pyecharts.charts import Timeline, Grid
from pyecharts.charts import Pie
from pyecharts import options as opts
from pyecharts.render import make_snapshot
df = pd.read_csv('C:/팀과제/스타벅스_크롤링.csv')
df

def extract_gu(address):
    # '구'가 포함된 부분을 찾기
    parts = address.split()
    for part in parts:
        if '구' in part:
            return part
    return None

df['구'] = df['주소'].apply(extract_gu)

# 각 '구' 별로 빈도수 세기
gu_counts = df['구'].value_counts().reset_index()
gu_counts.columns = ['구', '빈도수']

gu_counts

DataFrame으로 뽑아낸 것을 csv파일로 저장한다.

# gu_counts csv로 저장
file_path = r'C:\팀과제\스타벅스_구별_데이터.csv'  # 파일명.csv에는 저장하려는 파일명을 넣어주세요
gu_counts.to_csv(file_path, index=False, encoding='utf-8-sig')

print(f"DataFrame이 {file_path}에 저장되었습니다.")

스타벅스_구별_데이터.csv
0.00MB

이 DataFrame을 바탕으로 원형 파이차트로 뽑아낸다.

gu_counts.echart.pie(x='구', y='빈도수', figsize=(600,400), 
                     radius=['20%', '60%'], 
                     label_opts={'position':'outer'}, title='스타벅스 구별 현황', 
                     legend_opts={'pos_right':'0%','orient':'vertical'}).render()

IPython.display.HTML(filename='render.html')

확인결과 강남, 중구, 서초순으로 많은 스타벅스 매장이 많은 것을 확인했다. 이외에도 막대그래프로 차트를 뽑아냈다.

!pip install matplotlib
import matplotlib.pyplot as plt
# 한글폰트 설치
!sudo apt-get install -y fonts-nanum 
!sudo fc-cache -fv 
!rm ~/.cache/matplotlib -rf 

plt.rc('font', family='NanumBarunGothic')
x=gu_counts['구']
y=gu_counts['빈도수']
plt.figure(figsize=(8,5)) # size : 8*5
plt.title('스타벅스 구별 현황', fontsize=25) # 윗상단이 제목
plt.ylabel('갯수')
plt.barh(x, y, alpha=0.3, color='deeppink') # 막대그래프, alpha는 투명도조절
plt.show()

한글폰트 코드가 제대로 작동되지 않았다. 아마 visual Code환경의 문제인것 같다.