Files
my-lotto/lotto_auto_buy.py

328 lines
11 KiB
Python
Executable File

"""
동행복권(dhlottery.co.kr) 로또 6/45 자동 구매 스크립트
- 매주 금요일 자동 실행 (cron/스케줄러 연동)
- 5게임 자동 구매
- Selenium 기반 웹 자동화
사전 준비:
pip install selenium
Chrome 브라우저 + ChromeDriver 설치 필요
"""
import os
import sys
import time
import json
import logging
from datetime import datetime
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
# ─────────────────────────────────────────────
# 설정
# ─────────────────────────────────────────────
CONFIG = {
"USER_ID": os.environ.get("LOTTO_USER_ID", "hyeonggil2"),
"USER_PW": os.environ.get("LOTTO_USER_PW", "Rlaehdbs062$"),
"BUY_COUNT": 1, # 구매 게임 수 (1~10)
"HEADLESS": True, # True: 브라우저 창 숨김, False: 브라우저 창 표시
"LOG_FILE": "lotto_log.json",
}
# 로깅 설정
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("lotto_auto.log", encoding="utf-8"),
],
)
logger = logging.getLogger(__name__)
def create_driver():
"""Chrome WebDriver 생성"""
options = Options()
if CONFIG["HEADLESS"]:
options.add_argument("--headless=new")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-gpu")
options.add_argument("--window-size=1920,1080")
# 자동화 탐지 우회
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
driver = webdriver.Chrome(options=options)
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{"source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"},
)
driver.implicitly_wait(5)
return driver
def login(driver):
"""동행복권 사이트 로그인"""
logger.info("로그인 시도 중...")
driver.get("https://www.dhlottery.co.kr/login")
time.sleep(2)
# 아이디/비밀번호 입력
wait = WebDriverWait(driver, 10)
id_input = wait.until(EC.element_to_be_clickable((By.ID, "inpUserId")))
id_input.click()
id_input.send_keys(CONFIG["USER_ID"])
pw_input = wait.until(EC.element_to_be_clickable((By.ID, "inpUserPswdEncn")))
pw_input.click()
pw_input.send_keys(CONFIG["USER_PW"])
# 로그인 버튼 클릭
login_btn = wait.until(EC.element_to_be_clickable((By.ID, "btnLogin")))
login_btn.click()
# /login이 URL에서 완전히 사라질 때까지 대기 (최대 15초)
try:
WebDriverWait(driver, 15).until(lambda d: "/login" not in d.current_url)
except:
pass
logger.info(f"로그인 후 URL: {driver.current_url}")
# 로그인 성공 확인 (URL이 /login에서 벗어나면 성공)
if "/login" in driver.current_url:
logger.error("로그인 실패! 아이디/비밀번호를 확인하세요.")
return False
logger.info("로그인 성공!")
return True
def check_balance(driver):
"""예치금 잔액 확인"""
driver.get("https://www.dhlottery.co.kr/mypage/home")
time.sleep(2)
try:
balance_elem = driver.find_element(By.CSS_SELECTOR, "#totalAmt")
balance_text = balance_elem.text.replace(",", "").replace("", "").strip()
balance = int(balance_text) if balance_text.isdigit() else 0
logger.info(f"현재 예치금 잔액: {balance:,}")
return balance
except Exception as e:
logger.warning(f"잔액 확인 실패: {e}")
return -1
def buy_lotto(driver, game_count=5):
"""
로또 6/45 자동 구매
game_count: 구매할 게임 수 (1~10)
"""
logger.info(f"로또 {game_count}게임 자동 구매 시작...")
# 로또 구매 페이지 이동
driver.get("https://ol.dhlottery.co.kr/olotto/game/game645.do")
time.sleep(3)
# iframe 전환 (로또 구매 페이지는 iframe 내부에 있음)
try:
# 팝업 닫기 (있을 경우)
try:
close_btns = driver.find_elements(By.CSS_SELECTOR, ".close, .btn_close")
for btn in close_btns:
try:
btn.click()
time.sleep(0.5)
except:
pass
except:
pass
wait = WebDriverWait(driver, 10)
# "자동" 선택
auto_btn = wait.until(
EC.element_to_be_clickable((By.ID, "num2"))
)
auto_btn.click()
time.sleep(1)
# 게임 수 설정 (드롭다운에서 선택)
from selenium.webdriver.support.ui import Select
try:
select_elem = driver.find_element(By.ID, "amoundApply")
select = Select(select_elem)
select.select_by_value(str(game_count))
time.sleep(0.5)
except Exception:
# 드롭다운이 없는 경우 자동번호 버튼을 game_count번 클릭
for i in range(game_count):
auto_select_btn = driver.find_element(By.ID, "btnSelectNum")
auto_select_btn.click()
time.sleep(0.5)
# "자동번호 선택" 버튼 클릭
try:
select_num_btn = driver.find_element(By.ID, "btnSelectNum")
select_num_btn.click()
time.sleep(1)
except:
pass
# "구매하기" 버튼 클릭
buy_btn = wait.until(
EC.element_to_be_clickable((By.ID, "btnBuy"))
)
buy_btn.click()
time.sleep(2)
# 구매 확인 팝업 "확인" 클릭
confirm_btn = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "#popupLayerConfirm input[value='확인']"))
)
confirm_btn.click()
time.sleep(3)
logger.info("구매 요청 완료! 구매 결과를 확인합니다...")
# 구매 결과 확인
result = extract_purchase_result(driver)
return result
except Exception as e:
logger.error(f"구매 중 오류 발생: {e}")
return None
def extract_purchase_result(driver):
"""구매 결과(번호) 추출"""
result = {
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"games": [],
"status": "unknown",
}
try:
time.sleep(2)
# 회차 / 추첨일 추출
try:
result["round"] = driver.find_element(By.ID, "buyRound").text
result["draw_date"] = driver.find_element(By.ID, "drawDate").text
except:
pass
# 번호 추출: #reportRow li div.nums span
game_rows = driver.find_elements(By.CSS_SELECTOR, "#reportRow li")
for row in game_rows:
nums = row.find_elements(By.CSS_SELECTOR, "div.nums span")
numbers = [int(n.text) for n in nums if n.text.strip().isdigit()]
if numbers:
result["games"].append(numbers)
if result["games"]:
result["status"] = "success"
logger.info(f"구매 성공! {len(result['games'])}게임")
for i, game in enumerate(result["games"], 1):
logger.info(f" 게임 {i}: {game}")
else:
result["status"] = "completed_but_numbers_unknown"
logger.info("번호 추출 실패. 마이페이지에서 확인하세요.")
except Exception as e:
result["status"] = "error"
result["error"] = str(e)
logger.warning(f"결과 추출 중 오류: {e}")
return result
def save_log(result):
"""구매 기록 저장"""
log_file = CONFIG["LOG_FILE"]
history = []
if os.path.exists(log_file):
try:
with open(log_file, "r", encoding="utf-8") as f:
history = json.load(f)
except:
history = []
history.append(result)
with open(log_file, "w", encoding="utf-8") as f:
json.dump(history, f, ensure_ascii=False, indent=2)
logger.info(f"구매 기록이 {log_file}에 저장되었습니다.")
def main():
"""메인 실행 함수"""
logger.info("=" * 50)
logger.info("로또 6/45 자동 구매 스크립트 시작")
logger.info(f"실행 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
logger.info("=" * 50)
# 아이디/비밀번호 확인
if CONFIG["USER_ID"] == "여기에_아이디_입력" or CONFIG["USER_PW"] == "여기에_비밀번호_입력":
logger.error("아이디/비밀번호가 설정되지 않았습니다!")
logger.error("환경변수(LOTTO_USER_ID, LOTTO_USER_PW)를 설정하거나 스크립트 내 CONFIG를 수정하세요.")
sys.exit(1)
driver = None
try:
driver = create_driver()
# 1. 로그인
if not login(driver):
logger.error("로그인 실패로 종료합니다.")
sys.exit(1)
# 2. 잔액 확인
balance = check_balance(driver)
cost = CONFIG["BUY_COUNT"] * 1000
if balance >= 0 and balance < cost:
logger.error(f"예치금 부족! 현재: {balance:,}원, 필요: {cost:,}")
logger.error("동행복권 사이트에서 예치금을 충전해주세요.")
sys.exit(1)
# 3. 로또 구매
result = buy_lotto(driver, CONFIG["BUY_COUNT"])
# 4. 결과 저장
if result:
save_log(result)
else:
save_log({
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"status": "failed",
"error": "구매 결과를 확인할 수 없습니다.",
})
logger.info("스크립트 실행 완료!")
except Exception as e:
logger.error(f"예상치 못한 오류: {e}")
save_log({
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"status": "error",
"error": str(e),
})
finally:
if driver:
driver.quit()
logger.info("브라우저 종료")
if __name__ == "__main__":
main()