Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- controlNet
- Generative Models
- stable diffusion
- posco 채용
- colorization
- diffusion models
- Image Generation
- kt인적성
- 논문 리뷰
- manganinja
- 포스코 코딩테스트
- 코딩테스트
- KT
- 포스코 채용
- ip-adapter
- dp
- 과제형 코딩테스트
- DDPM
- classifier-free guidance
- ddim
- 프로그래머스
Archives
- Today
- Total
Paul's Grit
[Python] [Data Analysis] Selenium을 활용한 주유소 가격 웹크롤링 및 데이터 분석 본문
Data Analysis/Web Crawling
[Python] [Data Analysis] Selenium을 활용한 주유소 가격 웹크롤링 및 데이터 분석
Paul-K 2023. 8. 16. 20:15주유소 가격 분석¶
- 유류비 정보 사이트: https://www.opinet.co.kr/searRgSelect.do
Selenium 시작¶
- 다운로드 경로 추가
In [55]:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
options = webdriver.ChromeOptions()
prefs = {
'download.default_directory':'/home/haneol/dev_ws/EDA/data/',
'download.propt_for_download':False
}
options.add_experimental_option('prefs', prefs)
url = 'https://www.opinet.co.kr/searRgSelect.do'
driver = webdriver.Chrome(
service=Service('../driver/chromedriver-linux64/chromedriver'),
options=options
)
driver.get(url)
In [56]:
# 시도 목록 가져오기
from selenium.webdriver.common.by import By
sido = driver.find_element(By.ID, 'SIDO_NM0')
sido_list = sido.find_elements(By.TAG_NAME, 'option')
for idx, sido_name in enumerate(sido_list):
print(str(idx) + '. ' + sido_name.get_attribute('value'))
0. 1. 서울특별시 2. 부산광역시 3. 대구광역시 4. 인천광역시 5. 광주광역시 6. 대전광역시 7. 울산광역시 8. 세종특별자치시 9. 경기도 10. 충청북도 11. 충청남도 12. 전라북도 13. 전라남도 14. 경상북도 15. 경상남도 16. 제주특별자치도 17. 강원특별자치도
In [57]:
sido_names = [sido_name.get_attribute('value') for sido_name in sido_list]
sido_names
Out[57]:
['', '서울특별시', '부산광역시', '대구광역시', '인천광역시', '광주광역시', '대전광역시', '울산광역시', '세종특별자치시', '경기도', '충청북도', '충청남도', '전라북도', '전라남도', '경상북도', '경상남도', '제주특별자치도', '강원특별자치도']
In [58]:
# 불필요한 값 제거
sido_names = sido_names[1:]
sido_names
Out[58]:
['서울특별시', '부산광역시', '대구광역시', '인천광역시', '광주광역시', '대전광역시', '울산광역시', '세종특별자치시', '경기도', '충청북도', '충청남도', '전라북도', '전라남도', '경상북도', '경상남도', '제주특별자치도', '강원특별자치도']
In [59]:
print(sido_names[0])
sido.send_keys(sido_names[0])
서울특별시
In [60]:
# 시군구 정보 가져오기
gu = driver.find_element(By.ID, 'SIGUNGU_NM0')
gu_list = gu.find_elements(By.TAG_NAME, 'option')
gu_names = [gu_name.get_attribute('value') for gu_name in gu_list]
print(len(gu_names), gu_names)
26 ['', '강남구', '강동구', '강북구', '강서구', '관악구', '광진구', '구로구', '금천구', '노원구', '도봉구', '동대문구', '동작구', '마포구', '서대문구', '서초구', '성동구', '성북구', '송파구', '양천구', '영등포구', '용산구', '은평구', '종로구', '중구', '중랑구']
In [61]:
gu_names.remove('')
print(len(gu_names), gu_names)
25 ['강남구', '강동구', '강북구', '강서구', '관악구', '광진구', '구로구', '금천구', '노원구', '도봉구', '동대문구', '동작구', '마포구', '서대문구', '서초구', '성동구', '성북구', '송파구', '양천구', '영등포구', '용산구', '은평구', '종로구', '중구', '중랑구']
In [62]:
print(gu_names[15])
gu.send_keys(gu_names[15])
성동구
In [63]:
import time
from tqdm.notebook import tqdm
In [14]:
# implicitly_wait(3) : time.sleep()과 달리 정해진 시간안에 웹페이지를 load하면 바로 넘어가거나,
# 아니면 3초를 기다림
for gu_name in tqdm(gu_names[:3]):
gu = driver.find_element(By.ID, 'SIGUNGU_NM0')
gu.send_keys(gu_name)
driver.implicitly_wait(3)
0%| | 0/3 [00:00<?, ?it/s]
In [15]:
# 웹브라우저 다운로드 속도, 버튼 활성화 등에 따라 파일이 다운로드가 안되는 경우가 발생 가능
for gu_name in tqdm(gu_names):
gu = driver.find_element(By.ID, 'SIGUNGU_NM0')
gu.send_keys(gu_name)
driver.implicitly_wait(5)
driver.find_element(By.XPATH, '''//*[@id="glopopd_excel"]''').click()
0%| | 0/25 [00:00<?, ?it/s]
In [64]:
# 파일 개수로 다운로드 확인
from glob import glob
len(glob('../data/지역_*'))
Out[64]:
0
In [169]:
# 현재 잘못 다운된 파일들 모두 제거
import os
for file in glob('../data/지역_*'):
os.remove(file)
len(glob('../data/지역_*'))
Out[169]:
0
In [65]:
# WebDriverWait(driver, timeout=30).until(EC.element_to_be_clickable()
# element가 클릭가능해질 때까지 기다림
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
for gu_name in tqdm(gu_names):
gu = driver.find_element(By.ID, 'SIGUNGU_NM0')
gu.send_keys(gu_name)
save = WebDriverWait(driver, timeout=30).until(EC.element_to_be_clickable((By.XPATH, '''//*[@id="glopopd_excel"]''')))
ActionChains(driver).move_to_element(save).perform()
save.send_keys(Keys.ENTER)
time.sleep(10)
len(glob('../data/지역_*'))
0%| | 0/25 [00:00<?, ?it/s]
Out[65]:
25
In [66]:
len(glob('../data/지역_*'))
Out[66]:
25
In [69]:
from glob import glob
file_list = glob('../data/지역_*')
print(len(file_list))
file_list
25
Out[69]:
['../data/지역_위치별(주유소) (21).xls', '../data/지역_위치별(주유소) (3).xls', '../data/지역_위치별(주유소) (8).xls', '../data/지역_위치별(주유소) (16).xls', '../data/지역_위치별(주유소) (17).xls', '../data/지역_위치별(주유소) (12).xls', '../data/지역_위치별(주유소) (1).xls', '../data/지역_위치별(주유소) (23).xls', '../data/지역_위치별(주유소) (10).xls', '../data/지역_위치별(주유소) (20).xls', '../data/지역_위치별(주유소) (9).xls', '../data/지역_위치별(주유소) (15).xls', '../data/지역_위치별(주유소) (22).xls', '../data/지역_위치별(주유소) (13).xls', '../data/지역_위치별(주유소) (5).xls', '../data/지역_위치별(주유소) (2).xls', '../data/지역_위치별(주유소) (6).xls', '../data/지역_위치별(주유소) (7).xls', '../data/지역_위치별(주유소).xls', '../data/지역_위치별(주유소) (11).xls', '../data/지역_위치별(주유소) (14).xls', '../data/지역_위치별(주유소) (24).xls', '../data/지역_위치별(주유소) (4).xls', '../data/지역_위치별(주유소) (18).xls', '../data/지역_위치별(주유소) (19).xls']
In [70]:
driver.close()
In [74]:
import pandas as pd
data = pd.read_excel(file_list[0], header=2)
data.tail(10)
Out[74]:
지역 | 상호 | 주소 | 상표 | 전화번호 | 셀프여부 | 고급휘발유 | 휘발유 | 경유 | 실내등유 | |
---|---|---|---|---|---|---|---|---|---|---|
6 | 서울특별시 | 응암주유소 | 서울 은평구 응암로 163 | HD현대오일뱅크 | 02-303-1448 | Y | - | 1714 | 1614 | - |
7 | 서울특별시 | 불광주유소 | 서울 은평구 연서로 314 (불광동) | HD현대오일뱅크 | 02-382-5149 | Y | 1927 | 1717 | 1587 | - |
8 | 서울특별시 | 지에스칼텍스㈜ 녹번주유소 | 서울 은평구 통일로 600 (녹번동) | GS칼텍스 | 02-385-5002 | Y | 1984 | 1734 | 1639 | - |
9 | 서울특별시 | 대성산업㈜ 대성주유소 | 서울 은평구 통일로 642 | GS칼텍스 | 02-355-3434 | Y | 1959 | 1739 | 1669 | - |
10 | 서울특별시 | (주)명연에너지 수색훼미리주유소 | 서울 은평구 수색로 236 (수색동) | HD현대오일뱅크 | 02-374-8770 | Y | - | 1755 | 1625 | - |
11 | 서울특별시 | 성원이앤에스(주)은평지점 | 서울 은평구 통일로 968 (진관동) | GS칼텍스 | 02-352-4451 | Y | 1955 | 1758 | 1618 | - |
12 | 서울특별시 | 박석고개주유소 | 서울 은평구 통일로 945 (갈현동) | SK에너지 | 02-388-0477 | Y | - | 1758 | 1628 | - |
13 | 서울특별시 | 은평뉴타운주유소 | 서울 은평구 통일로 924 (불광동) | SK에너지 | 02-355-0349 | Y | - | 1758 | 1628 | 1445 |
14 | 서울특별시 | 뉴타운셀프주유소 | 서울 은평구 통일로 1031 | S-OIL | 02-355-3055 | Y | - | 1765 | 1635 | - |
15 | 서울특별시 | 코끼리주유소 | 서울 은평구 서오릉로 41 (녹번동) | 자가상표 | 02-384-4000 | N | - | 1818 | 1718 | - |
In [72]:
data_list = []
for file in file_list:
data = pd.read_excel(file, header=2)
data_list.append(data)
oil_data = pd.concat(data_list)
len(oil_data)
Out[72]:
440
In [73]:
oil_data.info()
<class 'pandas.core.frame.DataFrame'> Index: 440 entries, 0 to 26 Data columns (total 10 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 지역 440 non-null object 1 상호 440 non-null object 2 주소 440 non-null object 3 상표 440 non-null object 4 전화번호 440 non-null object 5 셀프여부 440 non-null object 6 고급휘발유 440 non-null object 7 휘발유 440 non-null int64 8 경유 440 non-null int64 9 실내등유 440 non-null object dtypes: int64(2), object(8) memory usage: 37.8+ KB
In [75]:
oil_data = pd.DataFrame({
'상호': oil_data['상호'],
'주소': oil_data['주소'],
'가격': oil_data['휘발유'],
'셀프': oil_data['셀프여부'],
'상표': oil_data['상표'],
})
oil_data.tail()
Out[75]:
상호 | 주소 | 가격 | 셀프 | 상표 | |
---|---|---|---|---|---|
22 | 성원이앤에스(주)영등포지점 | 서울 영등포구 국회대로52길 9-13 (영등포동7가) | 1988 | N | GS칼텍스 |
23 | 국회대로주유소 | 서울 영등포구 국회대로 746 (여의도동) | 2069 | N | HD현대오일뱅크 |
24 | 버드나룻길주유소 | 서울 영등포구 버드나루로 111 (당산동) | 2080 | N | SK에너지 |
25 | SK KH에너지 여의도주유소 | 서울 영등포구 국회대로 794 (여의도동) | 2160 | N | SK에너지 |
26 | SJ오일(주) 여의도주유소 | 서울 영등포구 은행로 64 | 2240 | N | GS칼텍스 |
In [76]:
oil_data.reset_index(drop=True, inplace=True)
oil_data.tail()
Out[76]:
상호 | 주소 | 가격 | 셀프 | 상표 | |
---|---|---|---|---|---|
435 | 성원이앤에스(주)영등포지점 | 서울 영등포구 국회대로52길 9-13 (영등포동7가) | 1988 | N | GS칼텍스 |
436 | 국회대로주유소 | 서울 영등포구 국회대로 746 (여의도동) | 2069 | N | HD현대오일뱅크 |
437 | 버드나룻길주유소 | 서울 영등포구 버드나루로 111 (당산동) | 2080 | N | SK에너지 |
438 | SK KH에너지 여의도주유소 | 서울 영등포구 국회대로 794 (여의도동) | 2160 | N | SK에너지 |
439 | SJ오일(주) 여의도주유소 | 서울 영등포구 은행로 64 | 2240 | N | GS칼텍스 |
In [77]:
oil_data['구'] = [address.split()[1] for address in oil_data['주소']]
oil_data.head()
Out[77]:
상호 | 주소 | 가격 | 셀프 | 상표 | 구 | |
---|---|---|---|---|---|---|
0 | 타이거주유소 | 서울 은평구 수색로 188 (증산동) | 1684 | Y | SK에너지 | 은평구 |
1 | ㈜오일닷컴 다회주유소 | 서울 은평구 증산로 441 | 1684 | Y | S-OIL | 은평구 |
2 | 지에스칼텍스(주)수색뉴타운주유소 | 서울 은평구 수색로 350 (수색동) | 1684 | Y | GS칼텍스 | 은평구 |
3 | 지에스칼텍스㈜ 서부주유소 | 서울 은평구 응암로 210 (응암동) | 1694 | Y | GS칼텍스 | 은평구 |
4 | (주)디오티디 은평유니콘주유소 | 서울 은평구 통일로 1151 (진관동) | 1694 | N | HD현대오일뱅크 | 은평구 |
In [78]:
oil_data['구'].unique(), len(oil_data['구'].unique())
Out[78]:
(array(['은평구', '강서구', '노원구', '성북구', '송파구', '마포구', '강동구', '중구', '동대문구', '용산구', '도봉구', '성동구', '종로구', '서대문구', '광진구', '강북구', '구로구', '금천구', '강남구', '동작구', '서초구', '중랑구', '관악구', '양천구', '영등포구'], dtype=object), 25)
In [80]:
oil_data['가격'] = oil_data['가격'].astype('float')
oil_data.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 440 entries, 0 to 439 Data columns (total 6 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 상호 440 non-null object 1 주소 440 non-null object 2 가격 440 non-null float64 3 셀프 440 non-null object 4 상표 440 non-null object 5 구 440 non-null object dtypes: float64(1), object(5) memory usage: 20.8+ KB
In [82]:
oil_data.reset_index(drop=True, inplace=True)
oil_data.head()
Out[82]:
상호 | 주소 | 가격 | 셀프 | 상표 | 구 | |
---|---|---|---|---|---|---|
0 | 타이거주유소 | 서울 은평구 수색로 188 (증산동) | 1684.0 | Y | SK에너지 | 은평구 |
1 | ㈜오일닷컴 다회주유소 | 서울 은평구 증산로 441 | 1684.0 | Y | S-OIL | 은평구 |
2 | 지에스칼텍스(주)수색뉴타운주유소 | 서울 은평구 수색로 350 (수색동) | 1684.0 | Y | GS칼텍스 | 은평구 |
3 | 지에스칼텍스㈜ 서부주유소 | 서울 은평구 응암로 210 (응암동) | 1694.0 | Y | GS칼텍스 | 은평구 |
4 | (주)디오티디 은평유니콘주유소 | 서울 은평구 통일로 1151 (진관동) | 1694.0 | N | HD현대오일뱅크 | 은평구 |
In [85]:
## 셀프 여부에 따른 가격 비교
import matplotlib.pyplot as plt
import seaborn as sns
import koreanize_matplotlib
oil_data.boxplot(column='가격', by='셀프', figsize=(6, 4))
Out[85]:
<Axes: title={'center': '가격'}, xlabel='셀프'>
In [86]:
plt.figure(figsize=(6, 4))
sns.boxplot(x='셀프', y='가격', data=oil_data, palette='Set1')
plt.grid(True)
plt.show()
In [87]:
plt.figure(figsize=(12, 6))
sns.boxplot(x='상표', y='가격', hue='셀프', data=oil_data, palette='Set3')
plt.grid(True)
plt.show()
지도 시각화¶
In [88]:
import json
import folium
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
In [89]:
# 가장 비싼 주유소 10개
oil_data.sort_values(by='가격', ascending=False).head(10)
Out[89]:
상호 | 주소 | 가격 | 셀프 | 상표 | 구 | |
---|---|---|---|---|---|---|
147 | 서남주유소 | 서울 중구 통일로 30 | 2742.0 | N | SK에너지 | 중구 |
177 | 서계주유소 | 서울 용산구 청파로 367 (청파동) | 2735.0 | N | GS칼텍스 | 용산구 |
322 | (주)만정에너지 삼보주유소 | 서울 강남구 봉은사로 433 (삼성동) | 2578.0 | N | GS칼텍스 | 강남구 |
146 | 필동주유소 | 서울 중구 퇴계로 196 (필동2가) | 2499.0 | N | GS칼텍스 | 중구 |
321 | (주)제이제이네트웍스 제이제이주유소 | 서울 강남구 언주로 716 | 2398.0 | N | HD현대오일뱅크 | 강남구 |
145 | 약수주유소 | 서울 중구 다산로 173 | 2378.0 | N | GS칼텍스 | 중구 |
176 | 한석주유소 | 서울 용산구 이촌로 164 | 2375.0 | N | SK에너지 | 용산구 |
218 | SK북악주유소 | 서울 종로구 평창문화로 137 | 2298.0 | N | SK에너지 | 종로구 |
217 | (주)대양씨앤씨 사직주유소 | 서울 종로구 사직로 65 (사직동) | 2290.0 | N | GS칼텍스 | 종로구 |
216 | (주)중앙에너비스 혜화주유소 | 서울 종로구 창경궁로35길 1 | 2287.0 | N | SK에너지 | 종로구 |
In [90]:
# 가장 저렴한 주유소 10개
oil_data.sort_values(by='가격', ascending=True).head(10)
Out[90]:
상호 | 주소 | 가격 | 셀프 | 상표 | 구 | |
---|---|---|---|---|---|---|
332 | 만남의광장주유소 | 서울 서초구 양재대로12길 73-71 | 1659.0 | Y | 알뜰(ex) | 서초구 |
0 | 타이거주유소 | 서울 은평구 수색로 188 (증산동) | 1684.0 | Y | SK에너지 | 은평구 |
1 | ㈜오일닷컴 다회주유소 | 서울 은평구 증산로 441 | 1684.0 | Y | S-OIL | 은평구 |
2 | 지에스칼텍스(주)수색뉴타운주유소 | 서울 은평구 수색로 350 (수색동) | 1684.0 | Y | GS칼텍스 | 은평구 |
3 | 지에스칼텍스㈜ 서부주유소 | 서울 은평구 응암로 210 (응암동) | 1694.0 | Y | GS칼텍스 | 은평구 |
4 | (주)디오티디 은평유니콘주유소 | 서울 은평구 통일로 1151 (진관동) | 1694.0 | N | HD현대오일뱅크 | 은평구 |
5 | 지에스칼텍스㈜ 신사제일주유소 | 서울 은평구 증산로 423 (신사동) | 1694.0 | Y | GS칼텍스 | 은평구 |
389 | 가로공원주유소 | 서울 양천구 가로공원로 165 (신월동) | 1696.0 | N | SK에너지 | 양천구 |
17 | 화곡역주유소 | 서울 강서구 강서로 154 (화곡동) | 1696.0 | Y | 알뜰주유소 | 강서구 |
16 | 이케이에너지(주) 강서주유소 | 서울 강서구 화곡로 273 (화곡동) | 1696.0 | Y | HD현대오일뱅크 | 강서구 |
In [91]:
import numpy as np
gu_data = pd.pivot_table(data=oil_data, index='구', values='가격', aggfunc=np.mean)
gu_data.head()
Out[91]:
가격 | |
---|---|
구 | |
강남구 | 1968.117647 |
강동구 | 1808.285714 |
강북구 | 1747.333333 |
강서구 | 1768.606061 |
관악구 | 1768.714286 |
In [92]:
geo_path = '../data/02. skorea_municipalities_geo_simple.json'
geo_str = json.load(open(geo_path, encoding='utf-8'))
map = folium.Map(location=[37.5502, 126.982], zoom_start=10.5, tiles='Stamen Toner')
In [93]:
map.choropleth(geo_data=geo_str,
data=gu_data,
columns=[gu_data.index, '가격'],
key_on='feature.id',
file_color='PuRd')
map
Out[93]:
Make this Notebook Trusted to load map: File -> Trust Notebook
'Data Analysis > Web Crawling' 카테고리의 다른 글
[Python] EDA Toy Project : 스타벅스 & 이디야 위치 정보 EDA (0) | 2023.09.05 |
---|---|
[Python] [Data Analysis] Selenium을 활용한 웹크롤링 (0) | 2023.08.16 |