Self Oil Station Price Analysis

YJ·2023년 4월 5일
0

▷ 오늘 학습 계획: EDA 강의(유가분석 3~5)

데이터 확보하기 위한 작업

  • 사이트 구조 확인
  • 목표: 서울시 주유소를 대상으로 셀프 주유소가 저렴한지 확인
  • 목표 데이터: 브랜드, 가격, 셀프 주유 여부, 위치

selenium으로 데이터 얻어오기

from selenium import webdriver
from selenium.webdriver.common.by import By

페이지 접근

url = "https://www.opinet.co.kr/searRgSelect.do"
driver = webdriver.Chrome("../driver/chromedriver.exe")
driver.get(url)
driver.get(url)  #메인 페이지로 접속되어, 해당 URL로 다시 접근하기

팝업창이 뜰 경우

driver.switch_to.window(driver.window_handles[-1])  #팝업창으로 화면 전환
driver.close()  #팝업창 닫기
driver.get(url)  #접근 페이지 다시 요청

함수

import time
def main_get():
  #페이지 접근
    url = "https://www.opinet.co.kr/searRgSelect.do"
    driver = webdriver.Chrome("../driver/chromedriver.exe")
    driver.get(url)
    time.sleep(3)
  #1_팝업창으로 화면 전환
    driver.switch_to.window(driver.window_handles[-1])
  #2_팝업창만 닫기
    driver.close()
    time.sleep(3)
  #메인화면 창으로 전환
    driver.switch_to.window(driver.window_handles[-1])
  #접근 URL 다시 요청
    driver.get(url)

지역(시/도) 리스트 확인

sido_list_raw = driver.find_element(By.ID, "SIDO_NM0")
sido_list_raw.text
sido_list = sido_list_raw.find_elements(By.TAG_NAME, "option")
len(sido_list)  #18개 시/도 데이터
sido_list[1].text  #서울
sido_list[1].get_attribute("value")  #서울특별시

지역(시/도) 리스트 가져오기

#1
sido_names = []
for option in sido_list:
    sido_names.append(option.get_attribute("value"))
sido_names
#2 list comprehension
sido_names = [option.get_attribute("value") for option in sido_list]
sido_names[:5]
#인덱스 0에 해당하는 값이 빈칸이여서 삭제하기
sido_names = sido_names[1:]
#sido_names.remove("")

지역을 '서울'로 지정하기

sido_list_raw.send_keys(sido_names[0])

시/군/구 리스트 확인

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:]
len(gu_names)  #25개

시/군/구 지정

gu_list_raw.send_keys(gu_names[10])

엑셀 파일로 저장하기

#1
driver.find_element(By.CSS_SELECTOR, "#glopopd_excel").click()
#2
driver.find_element(By.XPATH, '//*[@id="glopopd_excel"]').click()
#3
element_get_excel = driver.find_element(By.ID, "glopopd_excel")
element_get_excel.click()

모든 구의 데이터를 엑셀 파일로 저장

import time
from tqdm import tqdm_notebook
for gu in tqdm_notebook(gu_names):
    element = driver.find_element(By.ID, "SIGUNGU_NM0")
    element.send_keys(gu)
    time.sleep(3) 
    element_get_excel = driver.find_element(By.XPATH, '//*[@id="glopopd_excel"]').click()
    time.sleep(3)

주유소 가격 정보 정리하기

import pandas as pd
from glob import glob
glob("../data/지역_*.xls")  #파일 목록 한번에 가져오기

파일명 저장

stations_files = glob("../data/지역_*.xls")

파일 하나만 읽어보기

tmp = pd.read_excel(stations_files[0], header = 2)
tmp.tail(2)

concat(형식이 동일하고 연달아 붙이기만 하면 될 때)

tmp_raw = []
for file_name in stations_files:
    tmp = pd.read_excel(file_name, header=2)
    tmp_raw.append(tmp)
stations_raw = pd.concat(tmp_raw)

데이터 컬럼 생성하기

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

주소에서 '구' 정보만 가져오기

for eachAddress in stations["주소"]:
    print(eachAddress.split()[1])
stations["구"] = [eachAddress.split()[1] for eachAddress in stations["주소"]]

'구' 데이터 확인하기

stations["구"].unique(), len(stations["구"].unique())

'가격' object -> float 형변환 오류

stations["가격"] = stations["가격"].astype("float")
#ValueError: could not convert string to float: '-'

가격 정보가 없는 주유소 확인

stations[stations["가격"] == "-"]

가격 정보가 있는 주유소만 사용하기

stations = stations[stations["가격"] != "-"]
stations["가격"] = stations["가격"].astype("float")  #가격 데이터 형변환

인덱스 재정렬

stations.reset_index(inplace = True)
stations.tail()
del stations["index"]  #재정렬 하면서 index 컬럼이 또 생겨서 삭제

주유 가격 시각화

matplotlib 한글 대응

import matplotlib.pyplot as plt
import seaborn as sns
import platform
from matplotlib import font_manager, rc

get_ipython().run_line_magic("matplotlib", "inline")

path = "C:/Windows/Fonts/malgun.ttf"

if platform.system() == "Darwin":
    rc("font", family = "Arial Unicod MS")
elif platform.system() == "Windows":
    font_name = font_manager.FontProperties(fname=path).get_name()
    rc("font", family=font_name)
else:
    print("Unknown system")

pandas boxplot

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

seaborn boxplot

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()

지도 시각화

import json
import folium
import warnings
#경고 문구 안나오게 설정
warnings.simplefilter(action="ignore", category=FutureWarning)

가격에 따른 주유소 정렬

stations.sort_values(by="가격", ascending=False).head(10)
stations.sort_values(by="가격", ascending=True).head(10)

피벗테이블 생성

import numpy as np
gu_data = pd.pivot_table(data=stations, index="구", values = "가격", aggfunc=np.mean)
gu_data.head()

choropleth

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="Stamen Toner")
my_map.choropleth(
    geo_data = geo_str,
    data = gu_data,
    columns = [gu_data.index, "가격"],
    key_on = "feature.id",
    fill_color="PuRd"
)
my_map

📝 selenium 안내사항에서 코드 변경 사항을 봤는데 강의 들으면서 아무 생각 없이 이전 코드를 따라 적었다. 에러가 나서 한참 고민했는데 바뀐 코드로 썼더니 바로 실행됐다. 이전 코드를 보니까 더 헷갈리는거 같다.

from selenium.webdriver.common.by import By
driver.find_element(By.ID, "SIDO_NM0")

▷ 내일 학습 계획: EDA 강의(Naver API)

[이 글은 제로베이스 데이터 취업 스쿨의 강의 자료 일부를 발췌하여 작성되었습니다.]

0개의 댓글