티스토리 뷰

셀프 주유소는 정말 저렴할까

제 4장 셀프 주유소는 정말 저렴할까

구성 및 블로그 진행 과정

4-1 Selenium 사용하기
-------------------------------------------------------
4-2 서울시 구별 주유소 가격 정보 얻기
4-3 구별 주유 가격에 대한 데이터의 정리
4-4 셀프 주유소는 정말 저렴한지 boxplot으로 확인하기
-------------------------------------------------------
4-5 서울시 구별 주유 가격 확인
4-6 서울시 주유 가격 상.하위 10개 주유소 지도에 표기하기

출처: 파이썬으로 데이터 주무르기 by 민형기


이번 장에서는 아주 간단한 질문에 답하는 것을 함께 해보려고 한다. 바로 누군가 '셀프 주유소는 정말 저렴한가?'라고 물었다면 데이터를 만지는 사람들은 어떻게 해야 할까? 답은 간단하다. 주유소의 가격을 조사해서 셀프주유소와 아닌 주유소를 비교하면 그만이다. 요즘 데이터 분석이라고 하면 마케팅 분야에서만 언급되고, 또 그래서 데이터 분석가가 되는 조건 같은 인터넷에 떠도는 문서를 보면 마케팅을 모르면 데이터 분석을 하는 것이 의미가 없는 것처럼 표현되는 것이 안타깝다. 의견, 가설, 사실을 데이터로 표현하고 검증하는 것도 데이터 분석가가 하는 일이다. 이번에는 소위 팩트 체크라는 것을 데이터 분석으로 해보려고 한다. 주제는 셀프 주유소는 정말 저렴한지이다.

3장에서 우리는 Beautiful Soup를 사용했다. 그것만 가지고도 많은 일을 할 수 있었다. 그런데 몇 가지 문제로 인해 Beautiful Soup만으로는 접근할 수 없는 인터넷의 정보가 있다. 이번 장에서 다룰 주제가 그렇다. 우선 주유소의 가격을 비교하는 정부 사이트인 Opinet에 접속해서 정보를 모으자.

'주유소/충전소 찾기' 선택 -> 지역별을 들어가서 지역정보를 바꾸어 보아도 주소는 바뀌지 않는다. 이렇게 접근 주소가 없으면 Beautiful Soup에서는 처리할 수 없다. 그래서 사용하는 것이 바로 Selenium이다.

4-1 Selenium 사용하기

Selenium은 Anaconda에 포함된 모듈이 아니기 때문에 별도로 설치해야 한다. 터미널에서 pip install selenium만으로 쉽게 설치된다. 하지만 Selenium은 모듈 설치만으로는 동작하지 않는다. 사용하는 브라우저에 맞춰 웹 드라이버를 다운받아야 한다. 구글 크롬의 경우 크롬 웹 드라이버를 다운받는다. 혹은 구글에서 Chrome Driver라고 검색한다.

In [1]:
# Selenium에서 webdriver를 import한다.
from selenium import webdriver
In [6]:
#네이버에 접속
driver = webdriver.Chrome('chromedriver')
driver.get('http://naver.com')

크롬이 실행되면서 네이버 창이 뜨는데 다음 사진과 같이 'Chrome이 자동화된 테스트 소프트웨어에 의해 제어되고 있습니다.'라는 문구를 볼 수 있다.

In [3]:
from PIL import Image
Image.open('selenium1.png')
Out[3]:

위와 같이 크롬 드라이버에 의해 생성된 브라우저는 손으로 조작해서는 안된다. 코드를 작성할 때 혼선이 생길 수 있기 때문이다. 그래도 XPath를 확보한다든지, 태그를 확인한다든지 등의 이유로 크롬 개발자 도구를 실행해야 할 수 있다. 그럴 때는 별도로 인터넷 창을 실행해서 작업하길 바란다.

In [4]:
# Selenium은 화면을 캡쳐할 수 있다.
driver.save_screenshot('001.jpg')
C:\Users\whanh\AppData\Local\Continuum\anaconda3\lib\site-packages\selenium\webdriver\remote\webdriver.py:1031: UserWarning: name used for saved screenshot does not match file type. It should end with a `.png` extension
  "type. It should end with a `.png` extension", UserWarning)
Out[4]:
True
In [7]:
xpath='''//*[@id="account"]/div/a/i'''
driver.find_element_by_xpath(xpath).click()
In [5]:
Image.open('selenium2.png')
Out[5]:

네이버 로그인 정보를 입력하는 곳이 있다. 크롬 드라이버로 네이버에 로그인하고 싶다면 당연히 ID와 비밀번호를 입력해야 한다. 위 그림을 보면 ID를 입력하는 부분과 비밀번호를 입력하는 부분의 html 소스 코드를 확인해보면 id= 이라는 항목에 id 혹은 pw라고 되어 있다. Selenium이 제공하는 명령 중 find_element_by_id를 이용해서 id와 pw를 찾으면 된다. 이미 그 전에 어떤 글자가 입력되어 있을 수 있기 때문에 clear() 명령으로 해당 id가 위치한 입력창의 내용을 지우고 send_keys 명령으로 자신의 ID와 비밀번호를 입력하면 된다.

In [8]:
elem_login = driver.find_element_by_id('id')
elem_login.clear()
elem_login.send_keys('whanhee132')

elem_login = driver.find_element_by_id('pw')
elem_login.clear()
elem_login.send_keys('*******')
In [9]:
# 태그 오른쪽 클릭->Copy->xpath 밑에 붙여넣기
# find_element_by_xpath(xpath) : xpath 위치 찾기
# click() : 로그인 버튼 클릭
xpath = '''//*[@id="frmNIDLogin"]/fieldset/input'''
driver.find_element_by_xpath(xpath).click()
In [10]:
# 메일에 접근(주소변경)
driver.get('http://mail.naver.com')
In [22]:
# BeautifulSoup를 이용해서 페이지 내용 읽어오기
from bs4 import BeautifulSoup

html = driver.page_source # 현재 Selenium이 접근한 페이지 소스를 넘겨 받는다. 
soup = BeautifulSoup(html, 'html.parser')
In [12]:
# 메일을 보낸 사람이 나타나는 곳의 태그 확인
Image.open('selenium3.png')
Out[12]:
In [25]:
raw_list = soup.find_all('div', 'name _ccr(lst.from)')
raw_list
Out[25]:
[<div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36984) _stopDefault" href="#" title='"OK캐쉬백상품추천서비스 " &lt;info@okcashbagservice.com&gt;'>OK캐쉬백상품추천..</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36983) _stopDefault" href="#" title='"사람인 | 추천" &lt;avatar_matching@mailinfo.saramin.co.kr&gt;'>사람인 | 추천</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36982) _stopDefault" href="#" title='"사람인 | 신입공채" &lt;openrecruitmatching@mailinfo.saramin.co.kr&gt;'>사람인 | 신입공채</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36981) _stopDefault" href="#" title='"잡코리아" &lt;return@infoweb.jobkorea.co.kr&gt;'>잡코리아</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36980) _stopDefault" href="#" title='"고양시청" &lt;callcenter@goyang.go.kr&gt;'>고양시청</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36979) _stopDefault" href="#" title='"사람인 | 추천" &lt;avatarmatching@mailinfo.saramin.co.kr&gt;'>사람인 | 추천</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36978) _stopDefault" href="#" title='"DBGuide" &lt;newsletter@dbguide.net&gt;'>DBGuide</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36976) _stopDefault" href="#" title='"서울시청" &lt;inews11@seoul.go.kr&gt;'>서울시청</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36975) _stopDefault" href="#" title='"사람인 | 나의 검색" &lt;personalmatching@mailinfo.saramin.co.kr&gt;'>사람인 | 나의 검색</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36974) _stopDefault" href="#" title='"해커스인강 챔프스터디" &lt;help@champstudy.com&gt;'>해커스인강 챔프스터..</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36973) _stopDefault" href="#" title='"사람인 | 신입공채" &lt;openrecruitmatching@mailinfo.saramin.co.kr&gt;'>사람인 | 신입공채</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36972) _stopDefault" href="#" title='"사람인 | 추천" &lt;avatar_matching@mailinfo.saramin.co.kr&gt;'>사람인 | 추천</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36971) _stopDefault" href="#" title='"사람인 | 신입공채" &lt;openrecruitmatching@mailinfo.saramin.co.kr&gt;'>사람인 | 신입공채</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36970) _stopDefault" href="#" title='"잡아바" &lt;jobaba@gjf.or.kr&gt;'>잡아바</a></div>,
 <div class="name _ccr(lst.from)"><span class="blind">보낸 이:</span><a class="_c1(myContextMenu|showSenderContextLayer|list|36969) _stopDefault" href="#" title='"사람인 | 추천" &lt;avatarmatching@mailinfo.saramin.co.kr&gt;'>사람인 | 추천</a></div>]
In [26]:
# 보낸 사람들의 리스트 확보
send_list = [raw_list[n].find('a').get_text() for n in range(0, len(raw_list))]
send_list
Out[26]:
['OK캐쉬백상품추천..',
 '사람인 | 추천',
 '사람인 | 신입공채',
 '잡코리아',
 '고양시청',
 '사람인 | 추천',
 'DBGuide',
 '서울시청',
 '사람인 | 나의 검색',
 '해커스인강 챔프스터..',
 '사람인 | 신입공채',
 '사람인 | 추천',
 '사람인 | 신입공채',
 '잡아바',
 '사람인 | 추천']
In [27]:
# 실행된 크롬 드라이버 종료
driver.close()
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함