본문 바로가기
개발일지/selenium, BeautifulSoup, requests

selenium 오일 데이터 스크래핑 후 시각화 1 (판다스 데이터프레임까지)

by 다니엘의 개발 이야기 2022. 7. 6.
320x100

오일 정보 스크래핑은 정말 애먹었었고, 한달전쯤 봤을땐 어떻게 해야할지 감도 안오던 때가 있었다.

그래서 유데미의 selenium 강의를 추가로 들었었고, 그때 적당히 쌓인 기초에, 최근에 제로베이스 해설강의 까지 들으면서 "최소한"의 이해가 되었다. 고 판단되었다. 너무 뿌듯하다.

 

코드와 과정이 참 길다.

그리고 그대로 따라쓰기엔 불편한 코드도 많을테지만, 공부에 참고를 위해서 주석처리등 부분을 그대로 둔다.

 

답안코드에서 참고한 사항은 두가지다.

 

첫번째.

자꾸 index out of range 에러가 나길래

이 부분에 대해서만 배꼈고, 이 부분에 대한 이해는 아직 충분하지 않다.

좀 더 생각해봐야겠다.

 

두번째.

위도값, 경도값을 googlemaps 에 주소를 입력하여 얻어내는 과정을 거치는걸 모르겠어서

배껴썼다.

이것도 생각이 필요하다.

 

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

import selenium
import pandas as pd
import chromedriver_autoinstaller
import googlemaps
import time
import datetime

 

chromedriver_autoinstaller.install()

 

url = 'https://www.opinet.co.kr/searRgSelect.do'
driver = webdriver.Chrome('/Users/daniel_choi/opt/anaconda3/envs/ds_study/lib/python3.8/site-packages/chromedriver_autoinstaller/103/chromedriver')
driver.get(url)
driver.get(url)

# WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "select#SIDO_NM0"))).send_keys("서울")

 

# 화면 최대화, 보기 편하게 하려고 + 코드 긁어오기 용이하게
# selenium은 Beautifulsoup와 다르게, 화면에서 안보이는 부분이 스크래핑이 안되는 영역이 있다.
driver.maximize_window()

 

# 지역 첫번째

# len으로 확인해줬더니 option이 없을땐 그냥 길이가 1이여서 반복문에 적합하지 않았다.
sido_raw = driver.find_elements(By.CSS_SELECTOR, 'select#SIDO_NM0 > option')

sido_list = []
for i in sido_raw[1:]:
    sido_list.append(i.text)

 

# 메인 영역에 서울 값 넣어주기
seoul = driver.find_elements(By.CSS_SELECTOR, 'select#SIDO_NM0')[0]
seoul.send_keys(sido_list[0])

 

# 구 리스트 뽑기

gu_raw = driver.find_elements(By.CSS_SELECTOR, 'select#SIGUNGU_NM0 > option')

gu_list = []

for i in gu_raw[1:]:
    gu_list.append(i.text)

len(gu_list)

 

# 서브 영역에 구 리스트 값 넣어주기

gu_send = driver.find_elements(By.CSS_SELECTOR, 'select#SIGUNGU_NM0')[0]
gu_send.send_keys(gu_list[0])

 

# 반복 클릭을 위한 메인 영역 설정

# 구에 해당하는 총 갯수와 현재 설정한 태그 추적기의 숫자와 같은것을 확인했다.
# len(driver.find_elements(By.CSS_SELECTOR, 'tbody#body1 > tr'))

station_raw = driver.find_elements(By.CSS_SELECTOR, 'tbody#body1 > tr')

 

# 메인구간은 잡아 주었으니, 하나씩 어떻게 활용할지 테스트해보자

# 클릭
# station_raw[0].find_elements(By.CSS_SELECTOR, 'a')[0].click()

 

# 팝업안의 세계 - 첫번째 영역
# 이건 이렇게 구분을 해줘야 반복문 구분할때 편하다.

# 가게이름
# driver.find_elements(By.CSS_SELECTOR, 'label#os_nm')[0].text

 

# 팝업안의 세계 - 두번째 영역
# 실제로 영역이 구분되어있어서 이렇게 해줬다.

# 팝.두번째의 메인 뼈대
seconds = driver.find_elements(By.CSS_SELECTOR, 'div.overflow_gis_detail')[0]

# 주소
address = seconds.find_elements(By.CSS_SELECTOR, 'label#rd_addr')[0].get_attribute('innerHTML')

# 상표
brand = seconds.find_elements(By.CSS_SELECTOR, 'label#poll_div_nm')[0].get_attribute('innerHTML')

# 고급휘발유
fancyoil = seconds.find_elements(By.CSS_SELECTOR, 'label#b034_p')[0].get_attribute('innerHTML')

# 휘발유
oil = seconds.find_elements(By.CSS_SELECTOR, 'label#b027_p')[0].get_attribute('innerHTML')

# 경유
diesel = seconds.find_elements(By.CSS_SELECTOR, 'label#d047_p')[0].get_attribute('innerHTML')

 

# 팝업안의 세계 - 세번째 영역

# 부가정보

service_raw = driver.find_elements(By.CSS_SELECTOR, 'div.service > img')

# 지금의 나로써는 더 간단하게 할 방법이 생각이 안난다. 따라서 약간 노가다 한다.
wash = 'N' if 'off' in  service_raw[0].get_attribute('src') else 'Y'
charge = 'N' if 'off' in  service_raw[1].get_attribute('src') else 'Y'
repair = 'N' if 'off' in  service_raw[2].get_attribute('src') else 'Y'
convinien = 'N' if 'off' in  service_raw[3].get_attribute('src') else 'Y'
sel24 = 'N' if 'off' in  service_raw[4].get_attribute('src') else 'Y'

# 테스트
# print(wash, charge, repair, convinien, sel24)

 

# 위도, 경도는 그대로 가져다 썼다.

# googlemaps api key
keys = '구글 API 키'
gmaps = googlemaps.Client(key = keys)

# lat, lng
tmp = gmaps.geocode(address, language='ko')
lat = tmp[0].get('geometry')['location']['lat']
lng = tmp[0].get('geometry')['location']['lng']


# 본격 코드

# 밑작업 코드들

datas = []
# googlemaps api key
keys = '구글 API 키'
gmaps = googlemaps.Client(key = keys)

# 지역 첫번째

# len으로 확인해줬더니 option이 없을땐 그냥 길이가 1이여서 반복문에 적합하지 않았다.
sido_raw = driver.find_elements(By.CSS_SELECTOR, 'select#SIDO_NM0 > option')

sido_list = []
for i in sido_raw[1:]:
    sido_list.append(i.text)


# 메인 영역에 서울 값 넣어주기
seoul = driver.find_elements(By.CSS_SELECTOR, 'select#SIDO_NM0')[0]
seoul.send_keys(sido_list[0])

time.sleep(0.2)
# 구 리스트 뽑기

gu_raw = driver.find_elements(By.CSS_SELECTOR, 'select#SIGUNGU_NM0 > option')

gu_list = []

for i in gu_raw[1:]:
    gu_list.append(i.text)




### 본격 반복문 시작
for i in gu_list:
    # 서브 영역에 구 리스트 값 넣어주기
    gu_send = driver.find_elements(By.CSS_SELECTOR, 'select#SIGUNGU_NM0')[0]
    gu_send.send_keys(i)

    # 구에 해당하는 총 갯수와 현재 설정한 태그 추적기의 숫자와 같은것을 확인했다.
    # 팝업안의 세계 전역변수
    station_raw = driver.find_elements(By.CSS_SELECTOR, 'tbody#body1 > tr') 
    
    for s in range(len(station_raw)):
        # 클릭
        detail = f'#body1 > tr:nth-child({s+1}) > td.rlist > a'
        driver.find_elements(By.CSS_SELECTOR, detail)[0].click()

        # 팝업안의 세계 - 첫번째 영역
        
        # 가게이름
        name = driver.find_elements(By.CSS_SELECTOR, 'label#os_nm')[0].get_attribute('innerText')
        

        # 셀프 여부
        try:
            driver.find_elements(By.CSS_SELECTOR, 'img#self_icon')[0].get_attribute('alt')
            is_self = 'Y'
        except:
            is_self = 'N'
        # 팝업안의 세계 - 두번째 영역


        # 팝.두번째의 메인 뼈대
        seconds = driver.find_elements(By.CSS_SELECTOR, 'div.overflow_gis_detail')[0]
        # 주소
        address = seconds.find_elements(By.CSS_SELECTOR, 'label#rd_addr')[0].get_attribute('innerHTML')
        # 상표
        brand = seconds.find_elements(By.CSS_SELECTOR, 'label#poll_div_nm')[0].get_attribute('innerHTML')
        # 고급휘발유
        fancyoil = seconds.find_elements(By.CSS_SELECTOR, 'label#b034_p')[0].get_attribute('innerHTML')
        # 휘발유
        oil = seconds.find_elements(By.CSS_SELECTOR, 'label#b027_p')[0].get_attribute('innerHTML')
        # 경유
        diesel = seconds.find_elements(By.CSS_SELECTOR, 'label#d047_p')[0].get_attribute('innerHTML')


        # 팝업안의 세계 - 세번째 영역

        # 부가정보

        service_raw = driver.find_elements(By.CSS_SELECTOR, 'div.service > img')

        # 지금의 나로써는 더 간단하게 할 방법이 생각이 안난다. 따라서 약간 노가다 한다.
        wash = 'N' if 'off' in  service_raw[0].get_attribute('src') else 'Y'
        charge = 'N' if 'off' in  service_raw[1].get_attribute('src') else 'Y'
        repair = 'N' if 'off' in  service_raw[2].get_attribute('src') else 'Y'
        convinien = 'N' if 'off' in  service_raw[3].get_attribute('src') else 'Y'
        sel24 = 'N' if 'off' in  service_raw[4].get_attribute('src') else 'Y'


        # gu
        gu = address.split()[1]

        # lat, lng
        tmp = gmaps.geocode(address, language='ko')
        lat = tmp[0].get('geometry')['location']['lat']
        lng = tmp[0].get('geometry')['location']['lng']


        datas.append({
            'name' : name,
            'address' : address,
            'brand' : brand,
            'is_self' : is_self,
            'gasoline' : oil,
            'diesel' : diesel,
            'car_wash' : wash,
            'charging_station': charge,
            'car_maintenance': repair,
            'convinience_store' : convinien,
            '24_hour' : sel24,
            'gu' : gu,
            'lat': lat,
            'lng' : lng
        })

        time.sleep(0.3)
    time.sleep(0.5)
driver.quit()

 

len(datas)

 

df = pd.DataFrame(datas)
df.tail(10)

 

df.info()

 

df.to_csv('./oil_mine.csv', encoding='utf8')

 

df = pd.read_csv('./oil_mine.csv', index_col=0)
df.tail(2)
300x250