EDA 과제 2

JERRY·2025년 5월 19일

EDA

목록 보기
26/27
post-thumbnail

EDA/웹크롤링 과제 2회차

셀프 주유소는 더 저렴한가?

  • 셀프 주유소가 더 저렴한지 알아보기 위해 대한민국 주유 가격을 알아보는 사이트인 오피넷(https://www.opinet.co.kr)에 접속하여 서울시 주유정보를 가져와 가격 정보를 비교 분석

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

# 시/군/구  gu_list_raw->sigungu/gu_list->sigungu_option/gu_names->gu_list
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()   #24시간

전체 데이터 가져오기 전 테스트

# 구 선택
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()

#BeautifulSoup으로 현재페이지 읽어오기
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'
# 24시간 운영 여부
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

전체 데이터 수집

# 구별 주유소 조회 후 데이터 수집
# column 지정
station_name = [] #주유소명
address = []      #주소
brend = []        #주유소 브랜드
gasoline = []     #휘발유 가격
diesel = []       #경유 가격
self_yn = []      #셀프 여부
wash_yn = []      #세차장 여부
lpg_yn = []       #충전소 여부
maint_yn = []     #경정비 여부
cvs_yn = []       #편의점 여부
sel24_yn = []     #24시간 운영여부
gu = np.NaN       #구
lat = np.NaN      #위도 
lng = np.NaN      #경도

total_cnt = 0

# for문 이용해서 순차적으로 주유소 클릭
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')
        # 24시간
        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 셀프여부에 따른 주유 가격 비교

# boxplot(feat. pandas)
seoul_oil_df.boxplot(column="휘발유 가격", by="셀프 여부", figsize=(12, 8));

# boxplot(feat. seaborn)
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 브랜드별 셀프여부에 따른 주유가격 비교

# boxplot(feat. seaborn)

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

# boxplot(feat. seaborn)

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. 결과

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

0개의 댓글