EDA - 주유소 가격 정보 : 데이터 취업 스쿨 스터디 노트 12/7

slocat·2023년 12월 7일
0

start-data

목록 보기
35/75

목표 : 셀프 주유소가 더 저렴한지 확인하기

1. 데이터 확보

https://www.opinet.co.kr/searRgSelect.do
목표 데이터 : 브랜드, 가격, 셀프 주유 여부, 위치

마우스를 클릭해서 지역을 선택해도 웹 주소가 변하지 않고, HTML 소스에서 원하는 정보를 얻기 쉽지 않다. Beautiful Soup으로 바로 접근하기 어려우니 Selenium을 이용해보자!

1-1. URL에 접근하기

팝업창이 뜨는 경우 어떻게 대처해야 하는지도 알 수 있었다.

import time
from selenium import webdriver

def main_page_get():
    # 페이지 접근
    url = "https://www.opinet.co.kr/searRgSelect.do"
    driver = webdriver.Chrome(executable_path="../driver/chromedriver.exe")
    driver.get(url)
    time.sleep(3)

    # 팝업창으로 전환
    driver.switch_to_window(driver.window_handles[-1])

    # 팝업창 닫기
    driver.close()
    time.sleep(3)

    # 메인화면 창으로 전환
    driver.switch_to_window(driver.window_handles[-1])

    # 접근 URL 재요청
    driver.get(url)
    
main_page_get()

1-2. 시/도 데이터 가져오기

from selenium.webdriver.common.by import By
sido_list_raw = driver.find_element(By.ID, "SIDO_NM0")
sido_list = sido_list_raw.find_elements(By.TAG_NAME, "option")

sido_names = []
sido_names = [option.get_attribute("value") for option in sido_list]

sido_names = sido_names[1:]

sido_names
>>>
['서울특별시', '부산광역시', ... ]
# 시/도 선택하기
sido_list_raw.send_keys(sido_names[2])

1-3. 구 데이터 가져오기

gu_list_raw = driver.find_element(By.ID, "SIGUNGU_NM0")
gu_list = gu_list_raw.find_elements(By.TAG_NAME, "option")

gu_names = [option.get_attribute("value") for option in gu_list]
gu_names = gu_names[1:]

1-4. 엑셀 파일 다운로드

import time
from tqdm import tqdm_notebook

for gu in tqdm_notebook(gu_names):
    element = gu_list_raw = driver.find_element(By.ID, "SIGUNGU_NM0")
    element.send_keys(gu)
    time.sleep(3)
    
    element_get_excel = driver.find_element(By.ID, "glopopd_excel").click()
    time.sleep(3)

2. 데이터 전처리

2-1. 파일 목록 한 번에 가져오기

from glob import glob

# 파일명: 지역_위치별(주유소)
station_files = glob("../data/지역_*.xls")

2-2. 파일 이어 붙이기 : concat()

# 판다스로 파일 읽어오기
tmp_raw = []

for file_name in station_files:
    tmp = pd.read_excel(file_name, header=2)
    tmp_raw.append(tmp)

# 형식이 동일하고 연달아 붙이기만 하면 될 때 concat() 사용
station_raw = pd.concat(tmp_raw)
station_raw.info()

>>>
<class 'pandas.core.frame.DataFrame'>
Index: 438 entries, 0 to 31
Data columns (total 10 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   지역      438 non-null    object
 1   상호      438 non-null    object
 2   주소      438 non-null    object
 3   상표      438 non-null    object
 4   전화번호    438 non-null    object
 5   셀프여부    438 non-null    object
 6   고급휘발유   438 non-null    object
 7   휘발유     438 non-null    int64 
 8   경유      438 non-null    int64 
 9   실내등유    438 non-null    object
dtypes: int64(2), object(8)
memory usage: 37.6+ KB

2-3. 데이터프레임 정리하기

stations = pd.DataFrame({
    "상호": station_raw["상호"],
    "주소": station_raw["주소"],
    "가격": station_raw["휘발유"],
    "셀프": station_raw["셀프여부"],
    "상표": station_raw["상표"]
})

# "주소"에서 "구" 정보만 가져오기
stations["구"] = [eachAddress.split()[1] for eachAddress in stations["주소"]]

# 제대로 가져왔는지 확인
stations["구"].nunique() # 25

2-4. "가격" 데이터형 변환

stations["가격"] = stations["가격"].astype("float")

# 만약 에러가 발생한다면
# 가격 정보가 있는 주유소만 사용
stations = stations[stations["가격"] != "-"]
stations["가격"] = stations["가격"].astype("float")

마지막으로 인덱스를 재정렬하고, 필요 없는 컬럼을 삭제해준다.

# 인덱스 재정렬
stations.reset_index(inplace=True)

# "index" 컬럼 삭제
del stations["index"]

3. 주유 가격 시각화

# 준비
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import rc
import pandas as pd

plt.rcParams['axes.unicode_minus'] = False
rc("font", family="Malgun Gothic")
%matplotlib inline

3-1. box plot

  • Median 중심으로 위 아래 상자가 각각 전체 데이터의 25%씩 총 50%를 가진다.
  • IQR(Inter Quatile Range) : 상자 전체 길이
  • IQR의 1.5배 이상 벗어나면 점으로 별도 표기
# pandas
stations.boxplot(column="가격", by="셀프", figsize=(12, 8));

# seaborn
plt.figure(figsize=(12, 8))
sns.boxplot(x="셀프", y="가격", data=stations, palette="Set3")
plt.grid(True)
plt.show()
# 브랜드별 가격 비교
plt.figure(figsize=(12, 8))
sns.boxplot(x="상표", y="가격", hue="셀프", data=stations, palette="Set3")
plt.grid(True)
plt.show()

3-2. 지도 시각화

# 준비
import json
import folium
import numpy as np

# 경고 문구 나오는 게 싫다면
import warnings
warnings.simplefilter(action="ignore", category=FutureWarning)
# 가장 비싼 주유소 10개 고르기
stations.sort_values(by="가격", ascending=False).head()

# 가장 저렴한 주유소 10개 고르기
stations.sort_values(by="가격", ascending=True).head()
# 피벗 테이블 적용
gu_data = pd.pivot_table(
    data=stations,
    index="구",
    values="가격",
    aggfunc=np.mean
)
# 우리나라 경계선 좌표값이 담긴 데이터 가져오기
geo_path = "../data/02. skorea_municipalities_geo_simple.json"
geo_str = json.load(open(geo_path, encoding="utf-8"))

# 지도 준비
my_map = folium.Map(
    location=[37.5502, 126.982],
    zoom_start=10.5,
    tiles=tiles,
    attr=attr
)

# Choropleth
folium.Choropleth(
    geo_data=geo_str, # 우리나라 경계선 좌표값이 담긴 데이터
    data=gu_data,
    columns=[gu_data.index, "가격"],
    key_on="feature.id",
    fill_color="PuRd",
).add_to(my_map)

🚗🚗🚗

0개의 댓글