스터디 노트(EDA 4-2)

zoe·2023년 4월 5일
0

1. 셀레니움 설치

  • Selenium 설치
    - 윈도우, mac(intel) : conda install selenium
    - mac(m1) : pip install selenium

  • chromedriver

  • Beautiful Soup 만으로 해결할 수 없는 것
    - 접근할 웹 주소를 알 수 없을 때
    - 자바 스크립트를 사용하는 웹페이지의 경우
    - 웹 브라우저로 접근하지 않으면 안될 때

  • Selenium
    - 웹 브라우저로 원격 조작하는 도구
    - 자동으로 URL을 열고 클릭 등이 가능
    - 스크롤, 문자의 입력, 화면 캡처 등등

  • driver.quit() : 크롬 드라이버로 켠 창 끄기

#!pip install selenium
!pip install webdriver_manager

# !pip uninstall -y selenium # 셀레니움 삭제
from selenium import webdriver
#from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
# driver = webdriver.Chrome("../driver/chromedriver.exe") # 크롬 드라이버의 경로 지정
#driver = webdriver.Chrome(service = Service(ChromeDriverManager().install()))

# webdriver 경고문 해결 방법 :  https://yeko90.tistory.com/entry/%EC%85%80%EB%A0%88%EB%8B%88%EC%9B%80-%EA%B8%B0%EC%B4%88-executablepath-has-been-deprecated-please-pass-in-a-Service-object-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95
#driver.get("https://www.naver.com") # driver.get() : get명령으로 접근하고 싶은 주소 설정 


driver = webdriver.Chrome('../driver/chromedriver.exe') # mac : '../driver/chromedriver'
driver.get('https://www.naver.com')
driver.quit() # driver.quit() : 크롬 드라이버로 켠 창 끄기



2. 셀프 주유소가 정말 저렴하나요? - 데이터 확보하기 위한 작업


3. 셀레니움으로 접근

  • .find_elements(By.TAG_NAME, 'option') : tag 이용하기
  • get_attribute() : 속성 값 가져오기
from selenium import webdriver
from selenium.webdriver.chrome.options import Options #브라우저 창꺼짐 방지를 위한 모듈
from selenium.webdriver.common.by import By

# 브라우저 꺼짐 방지 옵션
# https://rimeestore.tistory.com/entry/%EC%85%80%EB%A0%88%EB%8B%88%EC%9B%80-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EA%B0%80-%EA%B3%84%EC%86%8D-%EA%BA%BC%EC%A7%88-%EB%95%8C
chrome_options = Options()
chrome_options.add_experimental_option("detach", True)
# 페이지 접근
# options=chrome_options : 브라우저 창꺼짐 방지 옵션 적용

url = 'https://www.opinet.co.kr/searRgSelect.do'
driver = webdriver.Chrome('../driver/chromedriver.exe') # mac : '../driver/chromedriver'
driver.get(url)

  • 문제
    - 1. 해당 url로 한 번에 접근이 안됨
    - 2. 메인 페이지로 접속이 되고, 팝업창이 나오는 경우 존재
# 팝업창 화면 전환 후 닫아주기 ★
# switch_to.window()로 변경됨(https://m.blog.naver.com/tkdtjdtkddms/221976262160)

driver.switch_to.window(driver.window_handles[-1])
driver.close() # 팝업창이 없을 경우에는 창이 꺼지니 주의해야 함
driver.get(url)

import time

# def main_get() 으로 함수로 적용해도 됨, 다만, 여기서는 해보니, 함수 안에 driver가 선언되어 그 후에 적용이 안되었음..ㅠㅠ
# 페이지 접근 2 (하나의 코드로 진행)

url = 'https://www.opinet.co.kr/searRgSelect.do'
driver = webdriver.Chrome('../driver/chromedriver.exe',options=chrome_options) 
# mac : '../driver/chromedriver'
# options=chrome_options : 브라우저 창꺼짐 방지 옵션 적용

driver.get(url)
time.sleep(3) # 페이지 띄운 후 3초 정지


# 팝업창으로 전환
#driver.switch_to.window(driver.window_handles[-1])
# 팝업창 닫아주기
#driver.close()
time.sleep(3) # 페이지 띄운 후 3초 정지


# 메인화면 창으로 전환
#driver.switch_to.window(driver.window_handles[-1])
# 접근 URL 다시 요청
driver.get(url)
#main_get() 
# 지역 : 시/도

sido_list_raw = driver.find_element(By.ID, 'SIDO_NM0')
sido_list_raw.text
# .find_elements(By.TAG_NAME, 'option') : tag 이용하기

sido_list = sido_list_raw.find_elements(By.TAG_NAME, 'option')
len(sido_list), sido_list[1].text
# get_attribute() : 속성 값 가져오기
sido_list[1].get_attribute('value')
sido_names=[]
for option in sido_list:
    sido_names.append(option.get_attribute('value'))
    
    
# 한줄 코드
#sido_names = [option.get_attribute('value') for option in sido_list]


sido_names
# 맨 앞 '' 빈칸 데이터 없애기
sido_names = sido_names[1:]
sido_names
sido_names[0]
# 웹 페이지의 시/도 변경 (서울)
sido_list_raw.send_keys(sido_names[0])
# 지역 : 시/군/구
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:]
len(gu_names)
gu_list_raw.send_keys(gu_names[15])
# 엑셀 저장
#driver.find_element(By.CSS_SELECTOR, '#glopopd_excel').click()
#driver.find_element(By.XPATH, '//*[@id="glopopd_excel"]').click()
element_get_excel = driver.find_element(By.ID,'glopopd_excel')
element_get_excel.click()
# 서울, 25개의 구 주유소 데이터 다운
import time
from tqdm import tqdm_notebook


for gu in tqdm_notebook(gu_names):
    element = driver.find_element(By.ID,'SIGUNGU_NM0')
    element.send_keys(gu)
    time.sleep(3)
    
    element_get_excel = driver.find_element(By.XPATH, '//*[@id="glopopd_excel"]').click()
    time.sleep(3)
    
driver.close()



4. 데이터 정리하기

  • glob() : 파일 목록 한번에 가져오기
  • concat() : 여러 개의 동일한 dataframe 붙이기
  • astype() : 데이터형 변환

import pandas as pd
from glob import glob
# !pip install glob
# 파일 목록 한 번에 가져오기
# glob() : 파일 목록 한번에 가져오기
glob('../data/지역_*.xls')
# 파일명 저장

stations_files = glob('../data/지역_*.xls')
stations_files
# 하나만 읽어보기

tmp = pd.read_excel(stations_files[0], header = 2) 
# 엑셀 파일에 1, 2행에 데이터가 없어서 header = 2 설정
tmp.tail(2)
tmp_raw = []

for file_name in stations_files:
    tmp = pd.read_excel(file_name, header = 2)
    tmp_raw.append(tmp)
# concat() : 여러 개의 동일한 dataframe 붙이기

station_raw = pd.concat(tmp_raw)
station_raw
station_raw.info()
stations = pd.DataFrame(
    {
    "상호" : station_raw["상호"],
    "주소" : station_raw["주소"],
    "가격" : station_raw["휘발유"],
    "셀프" : station_raw["셀프여부"],
    "상표" : station_raw["상표"]
    })
stations.head()
stations["주소"][0]
for eachAddress in stations["주소"]:
    print(eachAddress.split()[1])
stations["구"] = [ eachAddress.split()[1] for eachAddress in stations["주소"]]
stations["구"].unique(), len(stations["구"].unique())
# stations[stations["구"] == "서울특별시"]
# stations.loc[stations["구"] =="서울특별시","구"] = "성동구"
# stations[stations["구"] == "특별시"]
# stations.loc[stations["구"] =="특별시","구"] = "도봉구"
# stations["구"].unique(), len(stations["구"].unique())
# 가격 데이터형 변환 object → float
# astype() : 데이터형 변환

stations["가격"] = stations["가격"].astype("float")
# 가격 정보가 없는 주유소 확인

stations[stations["가격"] == "-"]
# 가격 정보가 있는 주유소만 사용
stations = stations[stations["가격"] != '-']
stations.tail()
stations.info()
stations
# 인덱스 재정렬
stations.reset_index(inplace=True)
stations.tail()
stations.head()
del stations["index"]
stations.head()



5. 주유 가격 정보 시각화

  • boxplot
    - Median 중심으로 아래 위 상자가 각각 전체 데이터의 25%씩 총 50%를 가진다
    - 상자 전체 길이를 IQR(Inter Quatile Range)라고 한다
    - IPR의 1.5배 이상 벗어나면 점으로 별도 표기를 한다

import matplotlib.pyplot as plt
import seaborn as sns
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": # mac일 경우 한글 설정
    rc("font", family="Arial Unicode MS")
elif platform.system() == "Windows": #window일 경우 한글 설정
    font_name = font_manager.FontProperties(fname=path).get_name()
    rc("font", family = font_name)
else:
    print("Unknown system. sorry")
# boxplot(pandas)

stations.boxplot(column="가격", by="셀프", figsize=(12,8));
# boxplot(seabon)

plt.figure(figsize=(12, 8))
sns.boxplot(x="셀프", y="가격", data = stations, palette = "Set3")
plt.grid(True)
plt.show()
# 상표별 가격
# boxplot(seabon)

plt.figure(figsize=(12, 8))
sns.boxenplot(x="상표", y="가격", hue="셀프", data=stations, palette="Set3")
plt.grid()
plt.show()
# 지도 시각화
import json
import folium
import warnings
warnings.simplefilter(action="ignore", category=FutureWarning)
# 가장 비싼 주유소 10개
stations.sort_values(by="가격", ascending=False).head(10)
# 가장 싼 주유소 10개
stations.sort_values(by="가격", ascending=True).head(10)
import numpy as np
gu_data = pd.pivot_table(data=stations, index="구", values="가격", aggfunc=np.mean)
gu_data.head()
geo_path = "../data/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,
    columns=[gu_data.index, "가격"],
    key_on="feature.id",
    fill_color="PuRd"
)
my_map

💻 출처 : 제로베이스 데이터 취업 스쿨

profile
#데이터분석 #퍼포먼스마케팅 #데이터 #디지털마케팅

0개의 댓글