# 페이지 접근
url = 'https://www.opinet.co.kr/searRgSelect.do'
driver = webdriver.Chrome()
driver.get(url)
# 지역: 시/도
sido_list_raw = driver.find_element(By.ID, 'SIDO_NM0')
sido_list_raw.text
sido_list_raw.send_keys('서울')
# 구
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:]
# 구 검색
gu_list_raw = driver.find_element(By.ID, 'SIGUNGU_NM0')
gu_list_raw.send_keys(gu_names[0])
os_name = [] # 주유소명
os_add = [] # 주소
os_brand = [] # 브랜드
os_normPrice = [] # 휘발유 가격
os_disPrice = [] # 경유 가격
os_slf = [] # 셀프
os_cwsh = [] # 세차장
os_charge = [] # 충전소
os_maint = [] # 경정비
os_cvs = [] # 편의점
os_sel24 = [] # 24시
os_gu = [] # 구
os_lat = [] # 위도
os_lng = [] # 경도
os_data = [
os_name, os_add, os_brand, os_normPrice, os_disPrice, os_slf, os_cwsh, os_charge, os_maint,
os_cvs, os_sel24, os_gu, os_lat, os_lng]
for gu in tqdm_notebook(gu_names):
# 구 검색
element = driver.find_element(By.ID, 'SIGUNGU_NM0')
element.send_keys(gu)
time.sleep(1)
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
# 검색할 주유소 개수
cnt = int(driver.find_element(By.ID, 'totCnt').text)
for i in range(1, cnt+1):
# 각 주유소 클릭
station = driver.find_element(By.CSS_SELECTOR, f'#body1 > tr:nth-child({i}) > td.rlist > a')
station.click()
# time.sleep(1)
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
data = soup.select('#os_dtail_info')[0]
# 이름
os_name.append(data.select_one('.header').text.strip())
# 주소
os_add.append(data.select_one('#rd_addr').text)
# 브랜드
os_brand.append(data.select_one('#poll_div_nm').text)
# 휘발유 가격
os_normPrice.append(data.select_one('#b027_p').text)
# 경유 가격
os_disPrice.append(data.select_one('#d047_p').text)
# 셀프
slf = data.select_one('#SPAN_SELF_VLT_YN_ID')
if type(slf.find('img')) == type(None):
os_slf.append('N')
else:
os_slf.append('Y')
# 세차
cswh = data.select_one('#cwsh_yn')['src']
if 'off' in cwsh:
os_cwsh.append('N')
else:
os_cwsh.append('Y')
# 충전소
charge = data.select_one('#lpg_yn')['src']
if 'off' in charge:
os_charge.append('N')
else:
os_charge.append('Y')
# 경정비
maint = data.select_one('#maint_yn')['src']
if 'off' in maint:
os_maint.append('N')
else:
os_maint.append('Y')
# 편의점
cvs = data.select_one('#cvs_yn')['src']
if 'off' in cvs:
os_cvs.append('N')
else:
os_cvs.append('Y')
# 24시 영업
sel24 = data.select_one('#sel24_yn')['src']
if 'off' in sel24:
os_sel24.append('N')
else:
os_sel24.append('Y')
# 구
os_gu.append(gu)
os = pd.DataFrame(os_data).transpose()
os_columns = [
'os_name', 'os_add', 'os_brand', 'os_normPrice', 'os_disPrice', 'os_slf', 'os_cwsh', 'os_charge', 'os_maint',
'os_cvs', 'os_sel24', 'os_gu', 'os_lat', 'os_lng']
import googlemaps
gmaps_key = "~"
gmaps = googlemaps.Client(key=gmaps_key)
for add in tqdm_notebook(os_add):
tmp = gmaps.geocode(add, language='ko')
lat = tmp[0].get('geometry')['location']['lat']
lng = tmp[0].get('geometry')['location']['lng']
os_lat.append(lat)
os_lng.append(lng)
os['os_lat'] = os_lat
os['os_lng'] = os_lng
def priceFloat(price):
price = price.replace(',', '')
price = float(price)
return price
os['os_normPrice'] = os['os_normPrice'].apply(priceFloat)
os['os_disPrice'] = os['os_disPrice'].apply(priceFloat)
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import font_manager, rc
%matplotlib inline
rc('font', family='Arial Unicode MS')
# boxplot pandas
os.boxplot(column='os_normPrice', by='os_slf');
# boxplot seaborn
sns.boxplot(x='os_slf', y='os_normPrice', data=os, palette='Set1')
plt.grid(True)
plt.show()
sns.boxplot(x='os_slf', y='os_disPrice', data=os, palette='Set2')
plt.grid(True)
plt.show()
휘발유와 경유 모두, 셀프 주유소가 셀프 주유소가 아닌 곳보다 가격이 높은 것을 볼 수 있다.
os['os_averagePrice'] = (os['os_normPrice'] + os['os_disPrice']) / 2
os.boxplot(column='os_averagePrice', by='os_slf');
os.boxplot(column='os_averagePrice', by='os_slf');
sns.boxplot(x='os_slf', y='os_averagePrice', data=os, palette='Set1')
plt.grid(True)
plt.show()
휘발유와 경유의 평균 값으로 그래프를 그려 보았다. 마찬가지로 셀프와 셀프가 아닌 주유소의 가격 차이를 볼 수 있다. 셀프 주유소는 대체로 셀프가 아닌 주유소보다 가격이 저렴하다.
import json
import folium
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
import numpy as np
# 구 별 가격 - 휘발유
gu_data_norm = pd.pivot_table(data=os, index='os_gu', values='os_normPrice', aggfunc=np.mean)
gu_data_norm.head()
# 구 별 가격 - 경유
gu_data_dis = pd.pivot_table(data=os, index='os_gu', values='os_disPrice', aggfunc=np.mean)
gu_data_dis.head()
# 구 별 가격 - 평균
gu_data_av = pd.pivot_table(data=os, index='os_gu', values='os_averagePrice', aggfunc=np.mean)
gu_data_av.head()
geo_path = './02. skorea_municipalities_geo_simple.json'
geo_str = json.load(open(geo_path, encoding='utf-8'))
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=10.5, tiles='Stamen Toner')
my_map.choropleth(
geo_data=geo_str,
data=gu_data_norm,
columns=[gu_data_norm.index, 'os_normPrice'],
key_on='feature.id',
fill_color='PuRd',
)
my_map
# swarmplot
plt.figure(figsize=(8, 6))
sns.swarmplot(x='os_slf', y='os_averagePrice', data=os, color='0', size=3)
plt.show()
가격이 높은 100개의 주유소 중 셀프 주유소가 얼마나 있는지 지도상으로 확인
my_map2 = folium.Map(location=[37.5502, 126.982], zoom_start=12, tiles='CartoDB positron')
for idx, rows in os.sort_values(by='os_averagePrice', ascending=False).head(100).iterrows():
if rows['os_slf'] == 'Y':
osColor = 'red'
elif rows['os_slf'] == 'N':
osColor = 'black'
folium.Circle(
location = [rows['os_lat'], rows['os_lng']],
radius=1,
color=osColor,
).add_to(my_map2)
my_map2
가격이 높은 순서대로 100개의 주유소를 정렬했을 때, 셀프 주유소가 셀프가 아닌 주유수보다 적은 것을 시각적으로 확인할 수 있다.
my_map3 = folium.Map(location=[37.5502, 126.982], zoom_start=12, tiles='CartoDB positron')
for idx, rows in os.sort_values(by='os_averagePrice', ascending=True).head(100).iterrows():
if rows['os_slf'] == 'Y':
osColor = 'red'
elif rows['os_slf'] == 'N':
osColor = 'black'
folium.Circle(
location = [rows['os_lat'], rows['os_lng']],
radius=1,
color=osColor,
).add_to(my_map3)
my_map3
가격이 낮은 순서대로 100개의 주유소를 정렬한 후, 셀프 여부를 살펴보았다. 셀프 주유소가 더 많은 것을 육안으로 확인할 수 있다.
추가적으로, 가격이 낮은 주유소들은 대체로 가격이 높은 주유소보다 외곽에 위치한다는 점을 알 수 있다.