맨땅에 헤딩하는 사람

Python Selenium을 이용한 로또 당첨 판매점 동적 웹크롤링 본문

파이썬/구현

Python Selenium을 이용한 로또 당첨 판매점 동적 웹크롤링

purplechip 2020. 7. 10. 21:21

로또 1등의 당첨 확률은 1/(45C6)으로 약 8백만 분의 1이다. 사막에서 바늘찾기와 같은 처참한 확률은 소위 말하는 '뽀록'이 제대로 터져야 당첨될 수 있다. 물론 복권당첨을 단순히 운의 영역으로 생각하지 않는 몇몇 사람들도 있으며 이들은 번호의 규칙성을 수학적, 통계학적으로 분석하여 다음 번호를 추론하려 한다.

 

그걸 보며 난 허황되지만 당첨점에 규칙성이 존재하지 않을까하는 생각을 하게 되었다. 동행복권 홈페이지에는 당첨점의 당첨횟수까지만 나와있으므로 당첨점이 몇 회차에 당첨되었는 지 데이터를 정리하기 위해서는 웹크롤링이 필요하다. 

[그림 1. 동행 복권 회차별 판매점 정보]

그림 1에서 보는 바와 같이 회차별로 당첨번호 배출점을 확인할 수 있다. 그러나 회차별 URL이 모두 https://dhlottery.co.kr/store.do?method=topStore&pageGubun=L645로 동일한데다가, 2등 배출점의 경우 번호를 클릭하며 데이터를 얻어야 하기에 동적웹크롤링이 필요하다. 그래서 나는 Selenium을 사용하였다.

Selenium이란 웹 애플리케이션 테스트를 위한 프레임워크인데 webdriver를 통해 실제 웹 브라우저를 동작시킬 수 있으므로 콤보 박스(drop down)나 번호 클릭 등이 필요한 동적 상황에서도 웹크롤링을 가능하게 해준다.

 

나는 크롬을 사용하였으며 아래 링크에서 본인의 크롬 버전에 맞는 드라이버를 다운받으면 된다.

https://chromedriver.chromium.org/downloads

 

코드의 골자는 BeautifulSoup를 이용하여 1등과 2등 배출점의 정보를 저장하고 드랍 다운 회차를 증가시키면서 반복하는 것이다.

 

드랍 다운 메뉴 옵션 선택 후 페이지 변경하기

아래 코드가 드랍 다운 메뉴 옵션에서 회차를 선택 후 페이지를 이동시키는 코드이다.

drw_no = 262
while True:
	select_drw = Select(driver.find_element_by_id("drwNo"))
    try:
        time.sleep(0.5)
        select_drw.select_by_value(str(drwNo))
        driver.find_element_by_css_selector('.btn_common.mid.blu').click()
    
    except selenium.common.exceptions.NoSuchElementException:
        break
        
    drwNo = drwNo + 1

 

[그림 2. 동행복권 회차별 판매점 페이지 내 드랍 다운(회차) HTML 코드]

Selenium에서 드랍 다운 옵션 선택할 때  click() 과  send_keys() 를 써서 선택하는 것은 불가능하다.

다행히 Selenium은  Select() 사용 후  select_by_value() select_by_index() select_by_visible_text() 와 같이 드랍 다운 옵션 선택이 가능하다. 만약 드랍 다운 옵션에 없는 값을 선택하면 NoSuchElementException이 발생하므로 마지막 회차 이후 값이 들어갈 경우에 대비해 예외처리를 해준다. time.sleep이 없으면 너무 빠르게 페이지가 넘어가면서 제대로 크롤링이 안되는 현상이 생긴다.

 

번호 클릭으로 페이지 변경하기

        page = 1
        while True:
            '''
            crowling...
            '''
            page = page + 1
            try:
                time.sleep(0.5)
                driver.find_element_by_xpath('//a[text()=' + str(page) + ']').click()
            except selenium.common.exceptions.NoSuchElementException:
                break

위와 마찬가지로 몇 페이지까지 존재하는 지 모르기 때문에 번호를 증가시키면서 클릭하다가 없는 번호가 나와 에러가 발생하면 예외처리 해준다.

 

구현코드

구현 코드는 다음과 같다.

import selenium
from selenium import webdriver
from selenium.webdriver.support.select import Select
from bs4 import BeautifulSoup
import time

driver = webdriver.Chrome('./chromedriver')
driver.get('https://dhlottery.co.kr/store.do?method=topStore&pageGubun=L645')

fst_store = []
sec_store = []
drwNo = 262

while True:
    select_drw = Select(driver.find_element_by_id("drwNo"))
    try:
        time.sleep(0.5)
        select_drw.select_by_value(str(drwNo))
        driver.find_element_by_css_selector('.btn_common.mid.blu').click()
        
        # first store crowling
        soup = BeautifulSoup(driver.page_source, 'html.parser')
        tables = soup.select('table.tbl_data.tbl_data_col > tbody')
        trs = tables[0].select('tr')
        for tr in trs:
            tds = tr.select('td')
            if not tr.select('.nodata'):
                fst_store.append([drwNo, tds[1].text, tds[2].text.strip(' \t\n'), tds[3].text])
                
        
        # second store crowling
        page = 1
        while True:
            soup = BeautifulSoup(driver.page_source, 'html.parser')    
            tables = soup.select('table.tbl_data.tbl_data_col > tbody')

            trs = tables[1].select('tr')
            for tr in trs:
                tds = tr.select('td')
                if not tr.select('.nodata'):
                    sec_store.append([drwNo, tds[1].text, tds[2].text])

            page = page + 1
            try:
                time.sleep(0.5)
                driver.find_element_by_xpath('//a[text()=' + str(page) + ']').click()
            except selenium.common.exceptions.NoSuchElementException:
                break
        
        
    except selenium.common.exceptions.NoSuchElementException:
        break
        
    drwNo = drwNo + 1
    
driver.close()
    
    
    
# Save data to excel format
import openpyxl

wb = openpyxl.Workbook()
fst_sheet = wb.active
fst_sheet.title = '1등 당첨점'

sec_sheet = wb.create_sheet('2등 당첨점')

for store in fst_store:
    fst_sheet.append(store)

for store in sec_store:
    sec_sheet.append(store)
    
wb.save('lotto.xlsx')

 

+사족

저장된 데이터를 바탕으로 서울, 인천에 위치한 이번에 당첨이 나올 것 같은 3곳을 골라 오천 원씩 로또를 직접 사봤다.

5등 하나 당첨되었다..

참고

드롭 다운 옵션 선택 

https://www.it-swarm-ko.tech/ko/python/selenium-python%ec%9d%84-%ec%82%ac%ec%9a%a9%ed%95%98%ec%97%ac-%eb%93%9c%eb%a1%ad-%eb%8b%a4%ec%9a%b4-%eb%a9%94%eb%89%b4-%ec%98%b5%ec%85%98-%ea%b0%92%ec%9d%84-%ec%84%a0%ed%83%9d%ed%95%98%eb%8a%94-%eb%b0%a9%eb%b2%95/941522973/

 

웹크롤링 전반적 방법

https://book.coalastudy.com/data-crawling/

 

'파이썬 > 구현' 카테고리의 다른 글

Python 도식추리 문제 출제 프로그램  (0) 2020.07.12
Comments