From 499237bb35d507023e89b1d327794dc08361c146 Mon Sep 17 00:00:00 2001 From: hyeonggil <> Date: Fri, 27 Mar 2026 23:53:22 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=95=88=EC=A0=84=ED=95=9C?= =?UTF-8?q?=20=ED=81=B4=EB=A6=AD=20=EB=B0=8F=20=EB=A0=88=EC=9D=B4=EC=96=B4?= =?UTF-8?q?=20=EC=95=8C=EB=A6=BC=20=EC=B2=98=EB=A6=AC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 레이어 알림이 존재할 경우 자동으로 닫는 기능을 보강하여 클릭 시 간섭 방지 - 클릭 시 팝업이 가로채는 경우를 처리하는 safe_click 함수 추가 - 자동번호 선택 및 구매 버튼 클릭 시 안전한 클릭 로직 적용 - 코드 가독성을 높이기 위해 주석 추가 --- lotto-runner/lotto_auto_buy.py | 78 +++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 15 deletions(-) diff --git a/lotto-runner/lotto_auto_buy.py b/lotto-runner/lotto_auto_buy.py index 120e683..a0eddd9 100644 --- a/lotto-runner/lotto_auto_buy.py +++ b/lotto-runner/lotto_auto_buy.py @@ -228,9 +228,55 @@ def buy_lotto(driver, game_count=5): except Exception: return False + def ensure_no_layer_alert(max_rounds: int = 3) -> None: + """popupLayerAlert가 떠 있으면 닫히는 것까지 보장(짧게 재시도).""" + for _ in range(max_rounds): + closed = close_layer_alert_if_present(timeout_s=0.6) + try: + WebDriverWait(driver, 1.2).until( + EC.invisibility_of_element_located((By.ID, "popupLayerAlert")) + ) + return + except Exception: + if not closed: + return + time.sleep(0.2) + + def safe_click(locator, timeout_s: float = 10, retries: int = 2) -> None: + """클릭이 팝업에 가로채이면 popupLayerAlert 닫고 재시도.""" + from selenium.common.exceptions import ElementClickInterceptedException + + last_exc = None + for _ in range(retries + 1): + ensure_no_layer_alert() + el = WebDriverWait(driver, timeout_s).until(EC.element_to_be_clickable(locator)) + try: + el.click() + return + except ElementClickInterceptedException as e: + last_exc = e + # 팝업이 클릭을 가로채는 경우가 대부분이라 닫고 재시도 + close_layer_alert_if_present(timeout_s=0.8) + time.sleep(0.3) + except Exception as e: + last_exc = e + break + + # 마지막 fallback: JS click + try: + ensure_no_layer_alert() + el = WebDriverWait(driver, timeout_s).until(EC.presence_of_element_located(locator)) + driver.execute_script("arguments[0].click();", el) + return + except Exception: + if last_exc: + raise last_exc + raise + # 첫 상호작용 전에 레이어 알림이 떠 있으면 먼저 닫기 if close_layer_alert_if_present(): logger.info("초기 popupLayerAlert 감지 — 닫기 처리 완료") + ensure_no_layer_alert() # 모든 레이어 팝업을 JS로 직접 숨기기 (z-index 오버레이 완전 제거) driver.execute_script(""" @@ -242,16 +288,14 @@ def buy_lotto(driver, game_count=5): time.sleep(0.5) logger.info("팝업 레이어 숨김 처리 완료") - # "자동" 탭 선택 — onclick 함수(selectWayTab)를 직접 호출 - # (간헐적으로 popupLayerAlert가 클릭을 가로채므로 전/후로 닫기 처리 + 1회 재시도) - for attempt in range(2): - close_layer_alert_if_present(timeout_s=0.5) + # "자동" 탭 선택 (id=num2). 팝업이 클릭을 가로채는 케이스 방지. + try: + safe_click((By.ID, "num2"), timeout_s=10, retries=2) + except Exception: + # fallback: 함수 직접 호출 + ensure_no_layer_alert() driver.execute_script("selectWayTab(1);") - time.sleep(1) - if not close_layer_alert_if_present(timeout_s=0.5): - break - logger.info("자동 탭 선택 중 popupLayerAlert 재발 — 닫기 후 재시도") - time.sleep(0.5) + time.sleep(1) # 게임 수 설정 (드롭다운에서 선택) from selenium.webdriver.support.ui import Select @@ -263,14 +307,12 @@ def buy_lotto(driver, game_count=5): except Exception: # 드롭다운이 없는 경우 자동번호 버튼을 game_count번 클릭 for i in range(game_count): - auto_select_btn = driver.find_element(By.ID, "btnSelectNum") - auto_select_btn.click() + safe_click((By.ID, "btnSelectNum"), timeout_s=10, retries=2) time.sleep(0.5) # "자동번호 선택" 버튼 클릭 try: - select_num_btn = driver.find_element(By.ID, "btnSelectNum") - select_num_btn.click() + safe_click((By.ID, "btnSelectNum"), timeout_s=10, retries=2) time.sleep(1) except: pass @@ -283,14 +325,20 @@ def buy_lotto(driver, game_count=5): buy_btn = wait.until( EC.element_to_be_clickable((By.ID, "btnBuy")) ) - buy_btn.click() + try: + buy_btn.click() + except Exception: + safe_click((By.ID, "btnBuy"), timeout_s=10, retries=2) time.sleep(2) # 구매 확인 팝업 "확인" 클릭 confirm_btn = wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR, "#popupLayerConfirm input[value='확인']")) ) - confirm_btn.click() + try: + confirm_btn.click() + except Exception: + safe_click((By.CSS_SELECTOR, "#popupLayerConfirm input[value='확인']"), timeout_s=10, retries=2) time.sleep(3) logger.info("구매 요청 완료! 구매 결과를 확인합니다...")