EDA 학습 과제 2 풀이 내용

허미생·2022년 8월 28일
0

노트

목록 보기
2/2

문제풀이 방법

  • 수집한 데이터들을 pandas 데이터 프레임으로 정리
  • 다시 한 번, 휘발유와 경유 가격이 셀프 주유소에서 정말 저렴한지 분석 결과를 작성
  • https://www.opinet.co.kr/searRgSelect.do

문제 1. 수집한 데이터들을 pandas 데이터 프레임으로 정리

1-1) requirements

# requirements
import time
from selenium import webdriver

1-2) selenium으로 접근

#페이지 접근
url = "https://www.opinet.co.kr/searRgSelect.do"
driver = webdriver.Chrome("../driver/chromedriver.exe")
driver.get(url)
time.sleep(0.5)
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") #여러개면 elements라고 s를 붙여줘야함!
len(sido_list), sido_list[0].text
#서울시 클릭
sido_names = [option.get_attribute("value") for option in sido_list]
sido_names = sido_names[1:]
sido_list_raw.send_keys(sido_names[0])

# 부가정보 4개모두 클릭

driver.find_element_by_xpath('//*[@id="CWSH_YN"]').click()
driver.find_element_by_xpath('//*[@id="MAINT_YN"]').click()
driver.find_element_by_xpath('//*[@id="CVS_YN"]').click()
driver.find_element_by_xpath('//*[@id="SEL24_YN"]').click()
#셀프 클릭해제
driver.find_element_by_xpath('//*[@id="SELF_DIV_CD"]').click()

# 여기부터 for문안에 들어갈 내용 체크하기
# 구
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:]

# 구 25개인지 확인
len(gu_names)
# 임의의 구 클릭
gu_list_raw.send_keys(gu_names[13])
# 조회버튼 클릭

driver.find_element_by_xpath('//*[@id="searRgSelect"]').click()

1-3) BeautifulSoup으로 데이터 가져오기

# requirements
from bs4 import BeautifulSoup
# for문 돌리기 전 코드 확인
#현재 화면의 전체 html코드 불러오기
req = driver.page_source 
soup = BeautifulSoup(req, "html.parser")
soup
#이름 휘발유 경유 정보만 추출하기
import pandas as pd
table = soup.select('div#os_price1>table.tbl_type10')
df_raw = pd.read_html(str(table))[0] #str()로 table을 묶어야 read_html()이 된다 + [0]번째로 불러와야 dataframe형식으로 불러온다.
#pd.read_html로 str(table)을 읽으면 table 형식으로 가져올/볼 수 있다. -> tbody를 가져오지않고 table을 가져온 이유
df_raw = df_raw.droplevel(axis=1,level=1)
df_raw
# img의 alt 속성에서 브랜드뽑기
# 주유소이름 클릭
driver.find_element_by_css_selector('#body1 > tr:nth-child(6) > td.rlist > a').click()
#soup2에 담기
req = driver.page_source 
soup2 = BeautifulSoup(req, "html.parser")
soup2
#soup2에서 주소
address = soup2.find(id="rd_addr").text
#soup2에서 브랜드
brand = soup2.find(id="poll_div_nm").text
#세차장여부
if soup2.find(id="cwsh_yn").attrs["src"] == "/images/user/gis/oil_station_service1_01.gif":
    cwsh = 'Y'
else:
    cwsh = 'N'
#lpg충전소 여부
if soup2.find(id="lpg_yn").attrs["src"] == "/images/user/gis/oil_station_service1_02.gif":
    lpg = 'Y'
else:
    lpg = 'N'
#lpg충전소 여부
if soup2.find(id="maint_yn").attrs["src"] == "/images/user/gis/oil_station_service1_03.gif":
    maint = 'Y'
else:
    maint = 'N'
#편의점 여부
if soup2.find(id="cvs_yn").attrs["src"] == "/images/user/gis/oil_station_service1_04.gif":
    cvs = 'Y'
else:
    cvs = 'N'
#24시간 여부
if soup2.find(id="sel24_yn").attrs["src"] == "/images/user/gis/oil_station_service1_05.gif":
    sel24 = 'Y'
else:
    sel24 = 'N'
#구이름
gu = address.split()[1]
#셀프여부
self='N'

address, brand, cwsh, lpg, maint, cvs, sel24, gu, self
#각각의 주유소이름들을 for문으로 돌리기위해 list로 가져오자
#len(soup.select("#body1>tr")) 개수 확인 완료
#클릭할 a태그 선택하는지 확인
oilstationlst = soup.select("#body1 > tr:nth-child(1) > td.rlist > a")
oilstationlst
driver.find_element_by_css_selector("#body1 > tr:nth-child(2) > td.rlist > a").click()
#주유소들 클릭하는지 확인하기
lengthis = len(soup.select("#body1>tr"))
for i in range(1, lengthis+1):
    driver.find_element_by_css_selector("#body1 > tr:nth-child("+str(i)+") > td.rlist > a").click()
    time.sleep(0.5)
# 이제 for문을 돌려보자
# 각 구의 주유소 정보 가져오기
from tqdm import tqdm_notebook

df = pd.DataFrame(index = ["휘발유", "경유"])
for gu in tqdm_notebook(gu_names):
    # 구 클릭
    element = driver.find_element_by_id("SIGUNGU_NM0")
    element.send_keys(gu)
    time.sleep(1)
    
    # 조회버튼 클릭
    driver.find_element_by_xpath('//*[@id="searRgSelect"]').click()
    time.sleep(1)
    
    # soup에 담기
    req = driver.page_source 
    soup = BeautifulSoup(req, "html.parser")
    soup
    
    # table을 dataframe으로 추출하기
    # 주유소명, 휘발유, 경유
    table = soup.select('div#os_price1>table.tbl_type10')
    df_raw = pd.read_html(str(table))[0]
    df_raw = df_raw.droplevel(axis=1,level=1)
    
    addresslst = []
    brandlst = []
    cwshlst = []
    lpglst = []
    maintlst = []
    cvslst = []
    sel24lst = []
    gulst = []
    selflst = []
    
    #브랜드, 세차장, 충전소, 경정비, 편의점, 24시영업, 구이름
    lengthis = len(soup.select("#body1>tr"))
    for i in range(1, lengthis+1):
        #주유소들 클릭하면서
        driver.find_element_by_css_selector("#body1 > tr:nth-child("+str(i)+") > td.rlist > a").click()
        time.sleep(0.5)
        #soup2에 담기
        req = driver.page_source 
        soup2 = BeautifulSoup(req, "html.parser")
        soup2
        #soup2에서 주소
        address = soup2.find(id="rd_addr").text
        addresslst.append(address)
        #soup2에서 브랜드
        brand = soup2.find(id="poll_div_nm").text
        brandlst.append(brand)
        #세차장여부
        if soup2.find(id="cwsh_yn").attrs["src"] == "/images/user/gis/oil_station_service1_01.gif":
            cwsh = 'Y'
        else:
            cwsh = 'N'
        cwshlst.append(cwsh)
        #lpg충전소 여부
        if soup2.find(id="lpg_yn").attrs["src"] == "/images/user/gis/oil_station_service1_02.gif":
            lpg = 'Y'
        else:
            lpg = 'N'
        lpglst.append(lpg)
        #경정비 여부
        if soup2.find(id="maint_yn").attrs["src"] == "/images/user/gis/oil_station_service1_03.gif":
            maint = 'Y'
        else:
            maint = 'N'
        maintlst.append(maint)
        #편의점 여부
        if soup2.find(id="cvs_yn").attrs["src"] == "/images/user/gis/oil_station_service1_04.gif":
            cvs = 'Y'
        else:
            cvs = 'N'
        cvslst.append(cvs)
        #24시간 여부
        if soup2.find(id="sel24_yn").attrs["src"] == "/images/user/gis/oil_station_service1_05.gif":
            sel24 = 'Y'
        else:
            sel24 = 'N'
        sel24lst.append(sel24)
        #구이름
        gu = address.split()[1]
        gulst.append(gu)
        #self여부(아까 체크해제함)
        self='N'
        selflst.append(self)
        
    df_raw["주소"] = addresslst
    df_raw["브랜드"] = brandlst
    df_raw["셀프 여부"] = selflst
    df_raw["세차장 여부"] = cwshlst
    df_raw["충전소 여부"] = lpglst
    df_raw["경정비 여부"] = maintlst
    df_raw["편의점 여부"] = cvslst 
    df_raw["24시간 운영 여부"] = sel24lst
    df_raw["구"] = gulst

    # df를 concat시키기
    df = pd.concat([df,df_raw], ignore_index=True)
    
df

1-4) 데이터 정리하기

# NaN값 날려버리기
df = df.dropna()
df.reset_index(inplace=True)
df
#index column 제거
del df["index"]
df
df
#이제 셀프주유소들을 가져오자... 탓할건 내 python 실력뿐
#셀프 클릭
driver.find_element_by_xpath('//*[@id="SELF_DIV_CD"]').click()
#나머지 클릭 해제
driver.find_element_by_xpath('//*[@id="NORM_YN"]').click()
driver.find_element_by_xpath('//*[@id="VLT_YN"]').click()
driver.find_element_by_xpath('//*[@id="KPETRO_YN"]').click()
driver.find_element_by_xpath('//*[@id="KPETRO_DP_YN"]').click()
#위에서 한 짓을 그대로~~ dfself에다가 해준다

dfself = pd.DataFrame(index = ["휘발유", "경유"])
for gu in tqdm_notebook(gu_names):
    # 구 클릭
    element = driver.find_element_by_id("SIGUNGU_NM0")
    element.send_keys(gu)
    time.sleep(1)
    
    # 조회버튼 클릭
    driver.find_element_by_xpath('//*[@id="searRgSelect"]').click()
    time.sleep(1)
    
    # soup에 담기
    req = driver.page_source 
    soup = BeautifulSoup(req, "html.parser")
    soup
    
    if soup.find(id='totCnt').text == 0 or soup.find(id='totCnt').text == "0":
        #셀프주유소가 없을경우 다음 지역을 탐색해라
        continue;
    
    # table을 dataframe으로 추출하기
    # 주유소명, 휘발유, 경유
    table = soup.select('div#os_price1>table.tbl_type10')
    df_raw = pd.read_html(str(table))[0]
    try:
        df_raw = df_raw.droplevel(axis=1,level=1)
    except:
        print(str(gu),'에 self 주유소가 없음;;')
    
    addresslst = []
    brandlst = []
    cwshlst = []
    lpglst = []
    maintlst = []
    cvslst = []
    sel24lst = []
    gulst = []
    selflst = []
    
    #브랜드, 세차장, 충전소, 경정비, 편의점, 24시영업, 구이름
    lengthis = len(soup.select("#body1>tr"))
    for i in range(1, lengthis+1):
        #주유소들 클릭하면서
        driver.find_element_by_css_selector("#body1 > tr:nth-child("+str(i)+") > td.rlist > a").click()
        time.sleep(0.5)
        #soup2에 담기
        req = driver.page_source 
        soup2 = BeautifulSoup(req, "html.parser")
        soup2
        #soup2에서 주소
        address = soup2.find(id="rd_addr").text
        addresslst.append(address)
        #soup2에서 브랜드
        brand = soup2.find(id="poll_div_nm").text
        brandlst.append(brand)
        #세차장여부
        if soup2.find(id="cwsh_yn").attrs["src"] == "/images/user/gis/oil_station_service1_01.gif":
            cwsh = 'Y'
        else:
            cwsh = 'N'
        cwshlst.append(cwsh)
        #lpg충전소 여부
        if soup2.find(id="lpg_yn").attrs["src"] == "/images/user/gis/oil_station_service1_02.gif":
            lpg = 'Y'
        else:
            lpg = 'N'
        lpglst.append(lpg)
        #경정비 여부
        if soup2.find(id="maint_yn").attrs["src"] == "/images/user/gis/oil_station_service1_03.gif":
            maint = 'Y'
        else:
            maint = 'N'
        maintlst.append(maint)
        #편의점 여부
        if soup2.find(id="cvs_yn").attrs["src"] == "/images/user/gis/oil_station_service1_04.gif":
            cvs = 'Y'
        else:
            cvs = 'N'
        cvslst.append(cvs)
        #24시간 여부
        if soup2.find(id="sel24_yn").attrs["src"] == "/images/user/gis/oil_station_service1_05.gif":
            sel24 = 'Y'
        else:
            sel24 = 'N'
        sel24lst.append(sel24)
        #구이름
        gu = address.split()[1]
        gulst.append(gu)
        #self여부(아까 체크함)
        #나는 지성인이다 통으로 바꿔보자
        
    df_raw["주소"] = addresslst
    df_raw["브랜드"] = brandlst
    df_raw["셀프 여부"] = 'Y'
    df_raw["세차장 여부"] = cwshlst
    df_raw["충전소 여부"] = lpglst
    df_raw["경정비 여부"] = maintlst
    df_raw["편의점 여부"] = cvslst 
    df_raw["24시간 운영 여부"] = sel24lst
    df_raw["구"] = gulst
   
    # df를 concat시키기
    dfself = pd.concat([dfself,df_raw], ignore_index=True)
    
dfself
# NaN값 날려버리기
dfself = dfself.dropna()
dfself.reset_index(inplace=True)
del dfself["index"]
dfself
#df와 dfself합치기
df = pd.concat([df,dfself], ignore_index=True)
df
#셀프이면서 동시에 일반인 주유소 즉 중복 주유소는 self가 Y인걸 두고 나머지를 제거
df.drop_duplicates(["주유소명"], keep='last', inplace=True, ignore_index=True)
df
df.to_csv("../data/[DS]eda2_hujaeeun_oilstation.csv", encoding="utf-8")
df
df = df[["주유소명", "주소", "브랜드", "휘발유", "경유", "셀프 여부", "세차장 여부", "충전소 여부", "경정비 여부", "편의점 여부", "24시간 운영 여부", "구",]]
df
#위도와 경도를 googlemaps로 가져오자
import googlemaps
gmaps_key = "AIzaSyCDmWqIHVxzOhK_Ijs1sotDvOeSWse0uj0"
gmaps = googlemaps.Client(key=gmaps_key)

import numpy as np
df["위도"] = np.nan
df["경도"] = np.nan

df_idx = df.set_index("주유소명")
df_idx
for idx, rows in tqdm_notebook(df_idx.iterrows()):
    name = df_idx.loc[idx, "주소"].split("(")[0]
    tmp = gmaps.geocode(name, language="ko")
    lat = tmp[0].get("geometry")["location"]["lat"]
    lng = tmp[0].get("geometry")["location"]["lng"]
    
    df_idx.loc[idx, "위도"] = lat
    df_idx.loc[idx, "경도"] = lng
df_idx
df["위도"] = list(df_idx["위도"])
df["경도"] = list(df_idx["경도"])
df
#파일 다시 저장하기
df.to_csv("../data/[DS]eda2_hujaeeun_oilstation.csv", encoding="utf-8")
df
#column이름 영어로 바꾸기
df.columns = ['name','address','brand','gasoline','diesel','self','cwsh','lpg','maint','cvs','sel24','gu','lat','lng']
df.to_csv("../data/[DS]eda2_hujaeeun_oilstation.csv", encoding="utf-8")
df

문제 2. 휘발유와 경유 가격이 셀프 주유소에서 정말 저렴한지 분석

1-1) requirements

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

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

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

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

1-2) 휘발유

# boxplot(feat.pandas)
df.boxplot(column="gasoline", by="self", figsize=(12, 8));
중간값으로 봤을 때, 서울시 휘발유 가격의 경우, 일반주유소의 중간값은 2000원 초반, 셀프주유소의 중간값은 1900원초반으로 셀프주유소가 약간 더 저렴하다. 현재 서울의 휘발유 가격을 보면 상위 50%의 경우 확실하게 일반주유소의 가격이 더 비싸다고 할 수 있다. 
따라서 확실한 결론은 <span style="color: blue">저점은 비슷하나 고점의 차이가 크므로,</span> <span style="color:blue;font-weight:bold">즉, 셀프주유소가 저렴한 편이다 까지는 말할 수 있다.</span>

1-3) 경유

# boxplot(feat.pandas)
df.boxplot(column="diesel", by="self", figsize=(12, 8));
중간값으로 봤을 때, 서울시 경유 가격의 경우, 일반주유소의 중간값은 약 2100원, 셀프주유소의 중간값은 약 2000원으로 셀프주유소가 약간 더 저렴하다. 현재 서울의 경유 가격을 보면 상위 50%의 경우 확실하게 일반주유소의 가격이 더 비싸다고 할 수 있다. 
따라서 확실한 결론은 <span style="color: blue">저점은 비슷하나 고점의 차이가 크기때문에,</span> <span style="color:blue;font-weight:bold">셀프주유소가 저렴한 편이다 까지는 말할 수 있다.</span>

힘들었던 점

  • 셀프 주유소를 분리하는 방법에 대해서 try catch로 만들 것을 생각하지 못해서 힘들었다.

느낀 점

  • 귀찮더라도 작은 것 하나부터 하다보면 프로젝트를 끝낼 수 있다
profile
미생 한마리 입니다 :D

0개의 댓글