[Python] Selenium을 활용한 동적 웹 크롤링
실시간 주유소 가격정보 수집과 분석
- 동적 웹크롤링
- 서울시의 주유소 가격정보 수집
- 주유소 가격 엑셀 데이터 정리
- 주유소 가격 정보 분석
- 셀프 주유소가 정말 저렴할까?
- 4대 주유 브랜드 별 가격 차이가 있을까?
- 서울에서 가장 비싼 주유소와 저렴한 주유소 통계
🚩 Selenium
Selenium을 이용한 데이터 수집과 시각화
- Beautiful soup을 이용한 스크래핑의 한계
- Beautiful soup으로 접근할 수 있는 데이터는 정적 웹 페이지만 가능→ 수집의 한계
- 자바 스크립트로 작성된 웹페이지
- 자바스크립트를 이용하여 함수 호출이 되어야 웹 크롤링이 가능함
- Selenium을 이용한 크롤링
- 웹브라우즈의 원격조정이 가능한 라이브러리
- 가상의 웹 브라우저를 띄우고 데이터 자동 수집
- 크롤링하지 못하도록 막은 사이트도 데이터 수집 가능
- 단순 반복되는 작업에 활용 → 업무 자동화에 활용
실시간 주유소 가격 비교 사이트 분석
- 셀프 주유소가 정말 저렴할까?
- 4대 주유 브랜드 별 가격 차이가 있을까?
- 서울에서 가장 비싼 주유소와 저렴한 주유소 통계
- Beautiful soup으로 접근할 수 있는 데이터는 정적 웹 페이지만 가능→ 수집의 한계
- 자바 스크립트로 작성된 웹페이지
- 자바스크립트를 이용하여 함수 호출이 되어야 웹 크롤링이 가능함
- 웹브라우즈의 원격조정이 가능한 라이브러리
- 가상의 웹 브라우저를 띄우고 데이터 자동 수집
- 크롤링하지 못하도록 막은 사이트도 데이터 수집 가능 - 단순 반복되는 작업에 활용 → 업무 자동화에 활용
- 오피넷에서는 매일 주유 데이터를 받아오는 것이 아니라, 고객들의 카드 정보를 통해서 데이터가 이 사이트에 쌓이고 반영된다.
- 자바스크립트로 클라이언트 화면에서의 동작을 통해서만 데이터를 뿌려주는 경우(오피넷), 동적 크롤링을 사용해야 한다.
Selenium 라이브러리 설치
- Selenium 모듈 추가
- 브라우저 웹 드라이버 다운로드 http://chromedriver.chromium.org/downloads
- 자신의 브라우저 버전과 동일한 파일 선택
우측 상단 세개 점 선택 => 도움말 => chrome 정보
- 자신의 브라우저 버전과 동일한 파일 선택
- 웹 드라이버 실행파일 복사
- 파이썬 프로그램 소스가 위치한 프로젝트에 복사
Selenium 테스트
- 사이트 접속하기
# 라이브러리 불러오기 from selenium import webdriver # 드라이버 연결 driver = webdriver.Chrome() # 웹사이트 이동 driver.get('http://www.naver.com/') # 브라우저 (드라이버) 종료 # driver.close()
오피넷 접속하기
- 화면 자동 클릭 구현하기
import pyautogui # 화면 자동클릭을 위한 라이브러리 사용 from selenium import webdriver driver = webdriver.Chrome() # 드라이버 연결 # 웹사이트 이동 driver.get('https://www.opinet.co.kr') # 직접 주유소 찾기 화면으로 갈수 없도록 되어 있음 print(pyautogui.position()) pyautogui.moveTo(1776, 893,1) # 절대 좌표로 1초 동안 이동 pyautogui.click()
- 시간 지연처리
import pyautogui from selenium import webdriver import time driver = webdriver.Chrome() # 드라이버 연결 # 웹사이트 이동 driver.get('https://www.opinet.co.kr') # 직접 주유소 찾기 화면으로 갈수 없도록 되어 있음 time.sleep(3) # driver.close() print(pyautogui.position()) pyautogui.moveTo(1776, 893,1) # 절대 좌표로 1초 동안 이동 pyautogui.click() time.sleep(1)
- 태그 분석
- 구 선택 id 정보 얻기
driver.find_element_by_id("SIDO_NM0").send_keys('서울특별시') # 1st 리스트 박스를 찾아서 강원도 선택 second_list_raw = driver.find_element_by_id("SIGUNGU_NM0") time.sleep(1) # 첫번째 리스트를 선택하고 대기 필수 second_list = second_list_raw.find_elements_by_tag_name("option") # 2nd 리스트 박스에서 옵션리스트 뽑기
# 리스트 만들기 option_values = [] for option in second_list: option_values.append(option.get_attribute("value")) print(option_values)
- 빈 문자 제거하기
option_values.remove('') # 빈 문자 제거 print(option_values)
- 시군구 옵션에 "강북구" 키 입력
second_list_raw.send_keys('강북구') # 키 입력을 강북구로 선택 # second_list_raw.send_keys(option_values[2]) # 키 입력을 강북구로 선택 time.sleep(3)
- 다운로드 버튼 클릭하기
# 엑셀 저장버튼 누르기, 저장확인 file_down = driver.find_element_by_id('glopopd_excel').click() time.sleep(3) # 강서구 다운로드 받아 보기 second_list_raw = driver.find_element_by_id("SIGUNGU_NM0") second_list_raw.send_keys('강서구') # 또는 option_values[3] time.sleep(3) file_down = driver.find_element_by_id('glopopd_excel').click() time.sleep(3)
- 반복문으로 모든 구 데이터 다운로드하기
# 강서구 다운로드 받아 보기 second_list_raw = driver.find_element_by_id("SIGUNGU_NM0") second_list_raw.send_keys('강서구') # 또는 option_values[3] time.sleep(3) file_down = driver.find_element_by_id('glopopd_excel').click() time.sleep(3)
# 자동 반복 다운로드 for cnt in range(len(option_values)): second_list_raw = driver.find_element_by_id("SIGUNGU_NM0") second_list_raw.send_keys(option_values[cnt]) # 키 입력을 차례대로 선택 time.sleep(3) file_down = driver.find_element_by_id('glopopd_excel').click()
🚩 데이터 분석 및 시각화
여러 개의 엑셀파일을 하나의 리스트에 담기
- 파일 이름과 경로 확인
import pandas as pd from glob import glob print(glob('지역*.xls')) # '지역'으로 시작되는 모든 xls 파일명을 리스트에 담기 merged_list = glob('지역*.xls') # 새로운 리스트에 저장 list_tabel = [] # 엑셀 내용을 담을 리스트 for file_name in merged_list: tmp = pd.read_excel(file_name, header=2) list_tabel.append(tmp) print(list_tabel) # 25개의 테이블이 저장된 리스트 total_gas_station = pd.concat(list_tabel) # 25개의 테이블을 하나의 리스트구조로 반환 print(total_gas_station)
필요한 정보를 DataFrame으로 가져오기
- 분석에 필요한 테이블 재구성
gas_station = pd.DataFrame({'주유소명': total_gas_station['상호'],\ '경유가격': total_gas_station['경유'],\ '셀프': total_gas_station['셀프여부'],\ '브랜드': total_gas_station['상표'],\ '주소': total_gas_station['주소']}) print(gas_station)
분석에 필요한 필드/데이터 가공
- 경유 가격이 없는 데이터 삭제
print(gas_station.info()) gas_station = gas_station[gas_station['경유가격'] != '-'] print(gas_station.info())
- 가격 정보를 실수형으로 변환
gas_station['경유가격'] = [float(value) for value in gas_station['경유가격']] print(gas_station.info())
- 1열 인덱스 번호 재지정
gas_station.reset_index(inplace=True) print(gas_station)
한글 출력 설정
- windows
# 한글 출력을 위한 폰트 설정 from matplotlib import font_manager, rc # 한글 시각화 패키지 설치 path = "c:/Windows/Fonts/malgun.ttf" font_name = font_manager.FontProperties(fname=path).get_name() rc('font', family=font_name)
- MAC
# 한글 출력을 위한 폰트 설정 from matplotlib import font_manager, rc # 한글 시각화 rc('font', family='AppleGothic')
시각화
- 셀프 VS 비셀프 가격 비교
import matplotlib.pyplot as plt gas_station.boxplot(column='경유가격', by='셀프') plt.show()
비셀프는 셀프에 비해 가격 평균과 분산이 크고 outlier가 많이 존재한다. 즉, 비셀프의 경우 주유소가 가지고 있는 환경에 따라 가격이 심하게 차이가 날 수 있다는 추측을 해볼 수 있다.
- 브랜드별 가격 분포
import seaborn as sns sns.boxplot(x='브랜드', y='경유가격', hue='셀프’, data=gas_station) plt.show()
우리가 익히 알고 있는 주류 주유 브랜드(GS칼텍스, SK에너지 S-OIL 등)의 가격 분포는 알뜰 주유소보다 더큰 가격 분포를 가지고 있으며, outlier또한 많이 존재한다. 그리고 특히 SK에너지가 가장 넓은 분포의 주유 값과 가장 큰 주유값의 주유소를 가지고 있다.
- 브랜드별 셀프 VS 비 셀프 가격 비교
sns.boxplot(x='셀프', y='경유가격', hue='브랜드', data=gas_station)
plt.show()
서울지역 최저, 최고 가격 통계
- 최고가격 10곳, 최저가격 10곳 출력
print(gas_station.sort_values(by='경유가격' , ascending=False).head(10))
print(gas_station.sort_values(by='경유가격', ascending=True).head(10))
Author And Source
이 문제에 관하여([Python] Selenium을 활용한 동적 웹 크롤링), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@changhtun1/Python-동적-웹-크롤링저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)