프로그래밍

[파이썬] 로또 당첨번호 크롤링 완벽 가이드 (동행복권 웹스크래핑)

밍키. 2024. 12. 6. 18:23

안녕하세요! 오늘은 파이썬을 사용하여 동행복권 사이트에서 역대 로또 당첨번호를 크롤링하는 방법을 공유하려고 합니다.

1. 개요

  • 동행복권 웹사이트에서 제공하는 로또 당첨번호 데이터를 수집
  • 병렬 처리를 통한 효율적인 데이터 수집
  • 안정적인 크롤링을 위한 재시도 로직 구현

2. 필요한 라이브러리

import requests
from datetime import datetime
import pandas as pd
from bs4 import BeautifulSoup
import concurrent.futures
import time
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

3. 주요 기능 구현

3.1 안정적인 세션 생성

# 재시도 설정
def create_session():
    """재시도 정책이 적용된 session 생성"""
    session = requests.Session()
    retry = Retry(
        total=5,  # 최대 재시도 횟수
        backoff_factor=1,  # 재시도 간 대기 시간
        status_forcelist=[500, 502, 503, 504]  # 재시도할 HTTP 상태 코드
    )
    adapter = HTTPAdapter(max_retries=retry)
    session.mount('http://', adapter)
    session.mount('https://', adapter)
    return session

3.2 최신 회차 정보 가져오기

def get_max_count():
    """최신 회차 번호를 크롤링하는 함수"""
    url = 'https://dhlottery.co.kr/common.do?method=main'
    session = create_session()
    try:
        response = session.get(url, timeout=30)
        response.raise_for_status()
        soup = BeautifulSoup(response.text, 'lxml')
        max_count = int(soup.find('strong', id='lottoDrwNo').text)
        return max_count
    except Exception as e:
        print(f"최신 회차 조회 중 오류 발생: {e}")
        return None
    finally:
        session.close()

3.3 회차별 당첨번호 크롤링

def crawling_lotto(count):
    """로또 당첨번호 정보를 조회하는 함수"""
    url = f'https://dhlottery.co.kr/gameResult.do?method=byWin&drwNo={count}'
    session = create_session()
    try:
        response = session.get(url, timeout=30)  # 타임아웃 시간 증가
        response.raise_for_status()
        soup = BeautifulSoup(response.text, 'lxml')
        date = datetime.strptime(soup.find('p', class_='desc').text, '(%Y년 %m월 %d일 추첨)')
        win_number = [int(i) for i in soup.find('div', class_='num win').find('p').text.strip().split('\n')]
        bonus_number = int(soup.find('div', class_='num bonus').find('p').text.strip())
        return {
            'date': date,
            'num1': win_number[0],
            'num2': win_number[1],
            'num3': win_number[2],
            'num4': win_number[3],
            'num5': win_number[4],
            'num6': win_number[5],
            'bonus': bonus_number
        }
    except Exception as e:
        print(f"{count}회차 크롤링 중 오류 발생: {e}")
        time.sleep(2)  # 오류 발생 시 2초 대기
        return None
    finally:
        session.close()

3.4 전체 데이터 수집

def fetch_all_lotto_data():
    """병렬 처리를 통해 모든 회차의 로또 데이터를 가져오는 함수"""
    max_count = get_max_count()
    if not max_count:
        return pd.DataFrame()
    rows = []
    # 동시 실행 worker 수 감소
    with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
        future_to_count = {executor.submit(crawling_lotto, i): i for i in range(1, max_count+1)}
        for future in tqdm(concurrent.futures.as_completed(future_to_count), total=max_count):
            result = future.result()
            if result:
                rows.append(result)
            time.sleep(0.1)  # 요청 간 약간의 간격 추가
    return pd.DataFrame(rows).sort_values('date')

4. 코드의 특징과 장점

  1. 안정성 강화
    • 재시도 로직 구현으로 일시적인 네트워크 오류 대응
    • 타임아웃 설정으로 무한 대기 방지
    • 오류 발생 시 적절한 대기 시간 부여
  2. 성능 최적화
    • ThreadPoolExecutor를 사용한 병렬 처리
    • 적절한 worker 수 설정 (max_workers=3)
    • 요청 간 간격 조정으로 서버 부하 고려
  3. 데이터 정확성
    • BeautifulSoup을 활용한 정확한 데이터 파싱
    • 예외 처리를 통한 누락 데이터 방지
    • pandas DataFrame으로 데이터 구조화

5. 사용 예시

# 데이터 수집 실행
data = fetch_all_lotto_data()

# 결과 확인
print(data.head())

6. 주의사항

  1. 웹 크롤링 시 서버에 과도한 부하를 주지 않도록 주의
  2. 동행복권의 웹사이트 구조가 변경될 경우 코드 수정 필요
  3. 상업적 용도로 사용 시 관련 법규 확인 필요

7. 마무리

이 코드를 통해 로또 당첨번호 데이터를 쉽고 안정적으로 수집할 수 있습니다. 수집한 데이터를 활용하여 다양한 분석이나 예측 모델을 만들어볼 수 있겠죠?

궁금하신 점이 있다면 언제든 연락주세요.

8. 참고자료

테디노트 - 로또 1회부터 최신회차까지 크롤링한 뒤 파일 저장하기(requests, beautifulsoup4)