EDA/웹크롤링 과제 2회차
셀프 주유소는 더 저렴한가?

01. 주유 정보 습득 및 데이터 전처리
필요한 모듈 import
import numpy as np
import pandas as pd
import seaborn as sns
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
import time
import tqdm
from tqdm.notebook import tqdm
import googlemaps
import folium
import json
오피넷 접속 후 필터링 설정


url = 'https://www.opinet.co.kr/searRgSelect.do'
driver = webdriver.Chrome()
driver.get(url)
sido_list_raw = driver.find_element(By.CSS_SELECTOR, '#SIDO_NM0 > option:nth-child(2)')
sido_list_raw.click()
sigungu = driver.find_element(By.CSS_SELECTOR, '#SIGUNGU_NM0')
sigungu_option = sigungu.find_elements(By.TAG_NAME, 'option')
gu_list = [option.get_attribute('value') for option in sigungu_option]
gu_list = gu_list[1:]
driver.find_element(By.CSS_SELECTOR, '#CWSH_YN').click()
driver.find_element(By.CSS_SELECTOR, '#MAINT_YN').click()
driver.find_element(By.CSS_SELECTOR, '#CVS_YN').click()
driver.find_element(By.CSS_SELECTOR, '#SEL24_YN').click()
전체 데이터 가져오기 전 테스트
element = driver.find_element(By.CSS_SELECTOR, '#SIGUNGU_NM0')
element.send_keys(gu_list[0])
oil_list = driver.find_element(By.CSS_SELECTOR, '#body1')
oils = oil_list.find_elements(By.TAG_NAME, 'a')
oils[0].click()
req = driver.page_source
soup = BeautifulSoup(req, 'html.parser')
detail = soup.select_one('#os_dtail_info > .inner')
station_name = detail.select_one('#os_nm').text.split()[-1]
address = detail.select_one('#rd_addr').text.split(' (')[0]
brend = detail.select_one('#poll_div_nm').text
gasoline = detail.select_one('#b027_p').text
diesel = detail.select_one('#d047_p').text
self = detail.select('#SPAN_SELF_VLT_YN_ID > #self_icon')
self_yn = 'Y' if self else 'N'
wash = detail.select_one('#cwsh_yn').get('src')
wash_yn = 'N' if 'off' in wash else 'Y'
lpg = detail.select_one('#lpg_yn').get('src')
lpg_yn = 'N' if 'off' in lpg else 'Y'
maint = detail.select_one('#maint_yn').get('src')
maint_yn = 'N' if 'off' in maint else 'Y'
cvs = detail.select_one('#cvs_yn').get('src')
cvs_yn = 'N' if 'off' in cvs else 'Y'
sel24 = detail.select_one('#sel24_yn').get('src')
sel24_yn = 'N' if 'off' in sel24 else 'Y'
print(station_name, address, brend, gasoline, diesel, self_yn, wash_yn, lpg_yn, maint_yn, cvs_yn, sel24_yn)
''' 세곡주유소 서울 강남구 헌릉로 731 SK에너지 1,609 1,495 Y Y N N N N
전체 데이터 수집
station_name = []
address = []
brend = []
gasoline = []
diesel = []
self_yn = []
wash_yn = []
lpg_yn = []
maint_yn = []
cvs_yn = []
sel24_yn = []
gu = np.NaN
lat = np.NaN
lng = np.NaN
total_cnt = 0
for gu in tqdm(gu_list):
element = driver.find_element(By.CSS_SELECTOR, '#SIGUNGU_NM0')
element.send_keys(gu)
time.sleep(1)
oil_list = driver.find_element(By.CSS_SELECTOR, '#body1')
oils = oil_list.find_elements(By.TAG_NAME, 'a')
total_cnt += int(driver.find_element(By.CSS_SELECTOR, '#totCnt').text)
time.sleep(1)
for oil in oils:
oil.click()
time.sleep(3)
req = driver.page_source
soup = BeautifulSoup(req, 'html.parser')
detail = soup.select_one('#os_dtail_info > .inner')
station_name.append(detail.select_one('#os_nm').text.split()[-1])
address.append(detail.select_one('#rd_addr').text.split(' (')[0])
brend.append(detail.select_one('#poll_div_nm').text)
gasoline.append(detail.select_one('#b027_p').text)
diesel.append(detail.select_one('#d047_p').text)
self = detail.select('#SPAN_SELF_VLT_YN_ID > #self_icon')
self_yn.append('Y' if self else 'N')
wash = detail.select_one('#cwsh_yn').get('src')
wash_yn.append('N' if 'off' in wash else 'Y')
lpg = detail.select_one('#lpg_yn').get('src')
lpg_yn.append('N' if 'off' in lpg else 'Y')
maint = detail.select_one('#maint_yn').get('src')
maint_yn.append('N' if 'off' in maint else 'Y')
cvs = detail.select_one('#cvs_yn').get('src')
cvs_yn.append('N' if 'off' in cvs else 'Y')
sel24 = detail.select_one('#sel24_yn').get('src')
sel24_yn.append('N' if 'off' in sel24 else 'Y')
driver.quit()
수집한 데이터를 DataFrame 형태로 설정
seoul_oil_df = pd.DataFrame({
'주유소명' : station_name,
'주소' : address,
'브랜드' : brend,
'휘발유 가격' : gasoline,
'경유 가격' : diesel,
'셀프 여부' : self_yn,
'세차장 여부' : wash_yn,
'충전소 여부' : lpg_yn,
'경정비 여부' : maint_yn,
'편의점 여부' : cvs_yn,
'24시간 운영여부' : sel24_yn,
'구': gu,
'위도' : lat,
'경도' : lng
})
seoul_oil_df

googlemaps를 이용하여 위도, 경도 데이터 수집
gmaps_key = "AIzaSyDYvdG8Mq3uKUIhe9dIX7P9865lUQVHCaA"
gmaps = googlemaps.Client(key=gmaps_key)
for idx, rows in tqdm(enumerate(seoul_oil_df['주소'])):
tmp = gmaps.geocode(rows, language='ko')
tmp[0].get("formatted_address")
tmp_gu = tmp[0].get("formatted_address")
lat = tmp[0].get('geometry')['location']['lat']
lng = tmp[0].get('geometry')['location']['lng']
seoul_oil_df.loc[idx, '위도'] = lat
seoul_oil_df.loc[idx, '경도'] = lng
seoul_oil_df.loc[idx, "구"] = tmp_gu.split()[2]
seoul_oil_df

데이터 타입 변경
seoul_oil_df['휘발유 가격'] = seoul_oil_df['휘발유 가격'].str.replace(',', '')
seoul_oil_df['휘발유 가격'] = seoul_oil_df['휘발유 가격'].astype('float64')
seoul_oil_df['경유 가격'] = seoul_oil_df['경유 가격'].str.replace(',', '')
seoul_oil_df['경유 가격'] = seoul_oil_df['경유 가격'].astype('float64')
seoul_oil_df.info()

저장
seoul_oil_df.to_csv('./01.seoul_oil_df.csv', sep=',', encoding='utf-8', index = False)
02. 휘발유와 경유 가격 분석
import matplotlib.pyplot as plt
import seaborn as sns
import platform
from matplotlib import font_manager, rc
plt.rcParams["axes.unicode_minus"] = False
rc("font", family="Malgun Gothic")
get_ipython().run_line_magic("matplotlib", "inline")
2-1 셀프여부에 따른 주유 가격 비교
seoul_oil_df.boxplot(column="휘발유 가격", by="셀프 여부", figsize=(12, 8));
plt.figure(figsize=(12, 8))
sns.boxplot(x="셀프 여부", y="경유 가격", data=seoul_oil_df, palette="Set1")
plt.grid(True)
plt.show()

sns.pairplot(seoul_oil_df,
x_vars=["셀프 여부"],
y_vars=["휘발유 가격", "경유 가격"])
plt.show()

2-2 브랜드별 셀프여부에 따른 주유가격 비교
plt.figure(figsize=(12, 8))
sns.boxplot(x="브랜드", y="경유 가격", hue="셀프 여부", data=seoul_oil_df, palette="Set2")
plt.grid(True)
plt.title("셀프 여부에 따른 브랜드별 경유 가격 분포")
plt.show()
plt.figure(figsize=(12, 8))
sns.boxplot(x="브랜드", y="휘발유 가격", hue="셀프 여부", data=seoul_oil_df, palette="Set3")
plt.grid(True)
plt.title("셀프 여부에 따른 브랜드별 휘발유 가격 분포")
plt.show()

2-3 구별 셀프여부에 따른 주유가격 비교
oil_price = pd.pivot_table(data=seoul_oil_df, index="구", columns="브랜드", values=["휘발유 가격","경유 가격"], aggfunc=np.mean)
oil_price.head()

plt.figure(figsize=(12, 8))
sns.boxplot(x="구", y="경유 가격", hue="셀프 여부", data=seoul_oil_df, palette="Set2")
plt.grid(True)
plt.xticks(rotation=45)
plt.title("셀프 여부에 따른 구별 경유 가격 분포")
plt.show()
plt.figure(figsize=(12, 8))
sns.boxplot(x="구", y="휘발유 가격", hue="셀프 여부", data=seoul_oil_df, palette="Set3")
plt.grid(True)
plt.xticks(rotation=45)
plt.title("셀프 여부에 따른 구별 휘발유 가격 분포")
plt.show()

03. 결과
- 셀프 여부에 따른 주유 가격 분석 결과는 실제로도 저렴하다 입니다.
- 구별, 주유소별, 주유 종류와 상관없이 모두 셀프 주유가 저렴한 것으로 보여집니다.
- 또한 셀프 주유 가격의 저렴하면서 가격의 폭이 좁은 반면 셀프 주유가 아닌 경우는 가격이 높으면서도 가격의 폭이 넓다는 사실도 추가적으로 알 수 있습니다.
- 강남구, 영등포구, 용산구, 중구 등 유난히 가격이 높은 구역도 있지만 이 구역에서 조차 셀프 주유가 아닌 곳에서의 가격이 높고 셀프 주유의 가격은 타 구역에 비하여 높지만 저렴한 가격대를 유지하고 있습니다.