1. 서울시 한정, 서울시 내에 있는 스타벅스의 매장 이름, 주소, 구를 가지고 와서 pandas data frame으로 정리하기
from bs4 import BeautifulSoup
import numpy as np
import pandas as pd
from selenium.webdriver.common.by import By
from selenium import webdriver
url = "https://www.starbucks.co.kr/store/store_map.do"
driver = webdriver.Chrome()
driver.get(url)
# 지역 검색
district_finding = driver.find_element(By.CSS_SELECTOR, '#container > div > form > fieldset > div > section > article.find_store_cont > article > header.loca_search > h3 > a')
district_finding.click()
# 서울 클릭
seoul_district = driver.find_element(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_district.click()
# 전체클릭
total_districts = driver.find_element(By.CSS_SELECTOR, '#mCSB_2_container > ul > li:nth-child(1) > a')
total_districts.click()
# 스타벅스 서울시 내 매장이름, 주소, 해당 매장이 속한 구 데이터 수집
req = driver.page_source
soup = BeautifulSoup(req, 'html.parser')
stores = soup.select('.quickResultLstCon')
names = []
address = []
districts = []
for s in stores:
name = s['data-name']
ad = s.find('p', class_='result_details').contents[0].strip()
gu = ad.split(' ')[1]
names.append(name)
address.append(ad)
districts.append(gu)
# 데이터 프레임화
df1 = pd.DataFrame({
'브랜드': '스타벅스',
'상호명': names,
'주소': address,
'구': districts
})
2. 서울시 한정, 서울시 내에 있는 이디야커피의 매장 이름, 주소, 구를 가지고 와서 pandas data frame으로 정리하기
# 이디아 매장
url = "https://members.ediya.com/store"
driver = webdriver.Chrome()
driver.get(url)
# 찾기로 커서 이동
driver.execute_script("selectSearchTab('storeAddr');")
# 이디아 검색창에 '서울 + 구'별 검색 후 매장(이름, 주소, 구) 추출하여 데이터프레임화, 같은 주소지만 이름이 다른 매장이 있어 데이터 처리 필요
from bs4 import BeautifulSoup
gu = df1['구'].unique()
ediya_stores = set()
ediya_address = []
ediya_gu = []
for g in gu:
key = '서울' + ' ' + g
driver.find_element(By.CSS_SELECTOR, '#keyword').send_keys(key)
driver.find_element(By.CSS_SELECTOR, '#contents > div > div > div.store_wrap > div.srch_wrap > div.form_search > button.btn_search').click()
req = driver.page_source
soup = BeautifulSoup(req, 'html.parser')
stores = soup.select('.st_li')
for s in stores:
name = s.find('h4', class_='name').text
addr = s.find('p', class_='addr').text
if name not in ediya_stores:
ediya_stores.add(name)
ediya_address.append(addr)
ediya_gu.append(g)
driver.find_element(By.CSS_SELECTOR, '#keyword').clear()
print(len(ediya_stores), len(ediya_address), len(ediya_gu))
df2 = pd.DataFrame({
'브랜드': '이디아',
'상호명': list(ediya_stores),
'주소': list(ediya_address),
'구': ediya_gu
})
3. 위의 2개의 데이터플레임을 바탕으로 분석 실시하기
from tqdm import tqdm_notebook
# 스타벅스 이디아 합치기
df_sum = pd.concat([df1, df2])
df_sum.reset_index(drop=True, inplace=True)
# google map API로 위, 경도 불러오기
import googlemaps
gmaps_key = "본인 키 입력"
gmaps = googlemaps.Client(key=gmaps_key)
# 위도 경도 추가
df_sum["위도"] = np.nan
df_sum["경도"] = np.nan
# 위도 경도 값 추가
for idx, rows in tqdm_notebook(df_sum.iterrows()):
tmp = gmaps.geocode(rows["주소"], language="ko")
if tmp:
lat = tmp[0].get("geometry")["location"]["lat"]
lng = tmp[0].get("geometry")["location"]["lng"]
df_sum.loc[idx, "위도"] = lat
df_sum.loc[idx, "경도"] = lng
else:
continue
import matplotlib.pyplot as plt
plt.rcParams["font.family"] = "Apple SD Gothic Neo"
df3 = df_sum.groupby(["구", "브랜드"]).size().unstack()
ax = df3.plot.bar(rot=0, figsize=(15,10), color=["red", "blue"])
ax.set_ylabel("매장 수")
위의 도표는 1과 2에서 서울시 내에 있는 스타벅스와 이디아 매장 개수를 데이터프레임한 결과 값을 바탕으로 구별 매장 수를 정리한 도표입니다.
분석 결과 스타벅스가 가장 많은 강남구에는 91개의 매장이 있고 해당 구의 이디아의 경우 29개의 매장이 있었습니다. 스타벅스가 가장 적은 중랑구에는 8개의 매장이 있었고 이디아의 경우 23개의 매장이 있었습니다. 따라서 매장 수만 놓고 보면 스타벅스가 있는 곳에 이디아 매장이 있다고 간주하는 것은 각 구별 편차가 있어서 그렇게 보기는 힘들었습니다.
하지만 구별 매장 개수만으로는 상관관계가 없다고 단정 짓기는 어려워 구별 타브랜드 매장들간의 거리를 모두 구해서 이 들 간의 평균을 구하여 계산하였습니다.
# 거리 계산 함수 정의
def calculate_distance(lat1, lon1, lat2, lon2):
# 위도와 경도를 라디안으로 변환
lat1_rad = np.radians(lat1)
lon1_rad = np.radians(lon1)
lat2_rad = np.radians(lat2)
lon2_rad = np.radians(lon2)
# 위도와 경도의 차이 계산
dlat = lat2_rad - lat1_rad
dlon = lon2_rad - lon1_rad
# 위도와 경도 차이의 제곱을 더한 후 제곱근을 계산하여 거리 계산
distance = 6371.01 * np.sqrt(dlat**2 + (np.cos(lat1_rad) * dlon)**2)
return distance
# 같은 구와 서로 다른 브랜드 값을 가진 인덱스 간의 거리 비교
max_distances = []
min_distances = []
average_distance = []
# 같은 구이면서 서로 다른 브랜드 인덱스 일 경우(중랑구 스타벅스가 인덱스일 때 중랑구 이디아 매장과 비교) 그 때의 최대거리, 최소거리, 평균거리를 구함
for idx1 in df_sum.index:
max_distance = -1
min_distance = float('inf')
average = []
for idx2 in df_sum.index:
if idx1 != idx2 and df_sum.loc[idx1, '구'] == df_sum.loc[idx2, '구'] and df_sum.loc[idx1, '브랜드'] != df_sum.loc[idx2, '브랜드']:
distance = calculate_distance(df_sum.loc[idx1, '위도'], df_sum.loc[idx1, '경도'], df_sum.loc[idx2, '위도'], df_sum.loc[idx2, '경도'])
max_distance = max(max_distance, distance)
min_distance = min(min_distance, distance)
average.append(distance)
max_distances.append(max_distance)
min_distances.append(min_distance)
average_distance.append(sum(average) / len(average))
# 최대 거리와 최소 거리를 새로운 열로 추가
df_sum['최대거리(km)'] = max_distances
df_sum['최소거리(km)'] = min_distances
df_sum['평균거리(km)'] = average_distance
# 구 및 브랜드 별 인덱스의 평균거리의 평균을 구함
d4 = df_sum.groupby(['구', '브랜드'])['평균거리(km)'].mean().reset_index()
# 시각화
d4['구 + 브랜드'] = d4['구'] + ' ' + d4['브랜드']
# 시각화
plt.figure(figsize=(10, 6))
plt.bar(d4['구 + 브랜드'], d4['평균거리(km)'], color='skyblue', alpha=0.7)
plt.title('구별 스타벅스 매장과 이디아 매장 간의 평균 거리')
plt.xlabel('구, 브랜드')
plt.ylabel('평균거리(km)')
plt.xticks(rotation=45, fontsize=7)
plt.tight_layout()
plt.show()
위의 도표는 각 구별 타브랜드 매장 간의 평균거리 입니다. 강남구 스타벅스와 이디아 매장 간 평균 이동 거리가 2.5km 인것을 확인 하실 수 있습니다. 서울시 내 스타벅스와 이디아 매장간 평균거리는 1.6km에서 2.9km 사이의 값인 것을 확인하실 수 있습니다.
따라서 구 별 매장 점포의 개수의 편차는 존재하지만 그 거리가 멀지 않다는 것을 확인할 수 있었습니다. 그러므로 이디야 커피가 스타벅스 커피 매장이 있는 곳 주변에 위치할까에 대한 생각은 어느 정도 타당하다는 결론을 내리게 되었습니다.
'프로젝트로 배우는 데이터사이언스' 카테고리의 다른 글
Tableau 실습 예제 (1, 2) (0) | 2024.05.17 |
---|---|
데이터 분석 과제 - 셀프 주유소는 정말 저렴할까? (0) | 2024.04.29 |