문제풀이 방법
문제 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로 만들 것을 생각하지 못해서 힘들었다.
느낀 점
- 귀찮더라도 작은 것 하나부터 하다보면 프로젝트를 끝낼 수 있다