diff --git a/config.ini b/config.ini index b4f4b581..5e33e9a7 100644 --- a/config.ini +++ b/config.ini @@ -34,21 +34,25 @@ price_selector_template = '//*[@id="productMainContentContainerId"]/div[1]/div[2 # 옵션 상자 ; option_box_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc' -option_box_selector = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[{index}]/div/div[1]/div/div[2]/div' +; option_box_selector = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[{index}]/div/div[1]/div/div[2]/div' +option_box_selector = 'div#productMainContentContainerId li > div > div:nth-child(1) > div > div:nth-child(2) > div' + ; excluded_option_marker = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-dfauwV.bXsMpn' excluded_option_marker = '.bXsMpn.sc-dfauwV' ; delete_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) span:has-text("삭제")' ; delete_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) div.sc-igZIGL.kQDmyq' ; delete_button_selector = '.kQDmyq.sc-igZIGL' +; delete_button_selector_template = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[{index}]/div/div[1]/div/div[2]/div/div[2]/div[1]/div/span' +delete_button_selector_template = 'div#productMainContentContainerId li:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > div.ant-row.ant-row-no-wrap.ant-row-space-between.ant-row-middle.css-1li46mu > div:nth-child(1) > div > span' -delete_button_selector_template = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[{index}]/div/div[1]/div/div[2]/div/div[2]/div[1]/div/span' fallback1_delete_button_selector_template = 'div#productMainContentContainerId li:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > div.ant-row.ant-row-no-wrap.ant-row-space-between.ant-row-middle.css-1li46mu > div:nth-child(1) > div > span' - +delete_dialog_selector = 'div.sc-ddjGPC.jbwEYW' +confirm_delete_button_selector = 'button.ant-btn-primary.ant-btn-dangerous:has-text('삭제')' ; confirm_delete_button_selector = '.ant-modal.css-1li46mu.ant-modal-confirm.ant-modal-confirm-confirm button:has-text("삭제")' -confirm_delete_button_selector = '/html/body/div[8]/div/div[2]/div/div[2]/div/div/div/div[2]/button[2]' +; confirm_delete_button_selector = 'xpath=/html/body/div[8]/div/div[2]/div/div[2]/div/div/div/div[2]/button[2]' add_button_selector2 = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-dRGYJT.hmQUGb' -add_button_selector = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[{index}]/div/div[1]/div/div[2]/div/div/img' +add_button_selector = 'div#productMainContentContainerId li:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > div > img' ; add_button_selector = 'div.hmQUGb.sc-dRGYJT' ; add_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-krITIZ.ckztYT' ; file_upload_button_selector = '.ant-modal-content button:has-text("클릭 or 드레그로 파일 업로드")' diff --git a/locatorManager.py b/locatorManager.py index 4118abaa..a8912579 100644 --- a/locatorManager.py +++ b/locatorManager.py @@ -92,6 +92,7 @@ class LocatorManager: 'excluded_option_marker': self.config.get('OptionLocators', 'excluded_option_marker').strip("'"), 'delete_button_selector_template': self.config.get('OptionLocators', 'delete_button_selector_template').strip("'"), 'fallback1_delete_button_selector_template': self.config.get('OptionLocators', 'fallback1_delete_button_selector_template').strip("'"), + 'delete_dialog_selector': self.config.get('OptionLocators', 'delete_dialog_selector').strip("'"), 'confirm_delete_button_selector': self.config.get('OptionLocators', 'confirm_delete_button_selector').strip("'"), 'add_button_selector': self.config.get('OptionLocators', 'add_button_selector').strip("'"), 'file_upload_button_selector': self.config.get('OptionLocators', 'file_upload_button_selector').strip("'"), diff --git a/option.py b/option.py index 0f8a6729..23bd5a6e 100644 --- a/option.py +++ b/option.py @@ -36,6 +36,7 @@ class OptionHandler: self.excluded_option_marker = self.locator_manager.get_locator('OptionLocators', 'excluded_option_marker') self.delete_button_selector_template = self.locator_manager.get_locator('OptionLocators', 'delete_button_selector_template') self.fallback1_delete_button_selector_template = self.locator_manager.get_locator('OptionLocators', 'fallback1_delete_button_selector_template') + self.delete_dialog_selector = self.locator_manager.get_locator('OptionLocators', 'delete_dialog_selector') self.confirm_delete_button_selector = self.locator_manager.get_locator('OptionLocators', 'confirm_delete_button_selector') self.add_button_selector = self.locator_manager.get_locator('OptionLocators', 'add_button_selector') self.file_upload_button_selector = self.locator_manager.get_locator('OptionLocators', 'file_upload_button_selector') @@ -268,6 +269,8 @@ class OptionHandler: if toggle_states['optionAutoSelect']: self.logger.debug(f"옵션 필터링 및 조정 : {toggle_states['optionAutoSelect']}") await self.filter_and_adjust_options(max_option_count) + # 가격 낮은 순 재정렬 클릭 + await self.low_order_click() # 6. 선택된 옵션정보 재수집 if self.is_percenty_success: @@ -629,6 +632,7 @@ class OptionHandler: try: selected_count = 0 # 현재까지 선택된 옵션 수 + self.logger.info(f"최대 선택 가능 옵션수 설정값 : {max_option_count}") for i, name in enumerate(self.option_info['original_names'].values()): # 최대 옵션 수에 도달하면 더 이상 선택하지 않음 if max_option_count > 0 and selected_count >= max_option_count: @@ -716,9 +720,9 @@ class OptionHandler: try: # 모든 옵션 상자 요소 가져오기 - # option_boxes = await self.page.query_selector_all(self.option_box_selector) + option_boxes = await self.page.query_selector_all(self.option_box_selector) - option_boxes = await self.page.query_selector_all("div#productMainContentContainerId li > div > div:nth-child(1) > div > div:nth-child(2) > div") + # option_boxes = await self.page.query_selector_all("div#productMainContentContainerId li > div > div:nth-child(1) > div > div:nth-child(2) > div") # option_image_element = await self.page.locator("xpath=//*[@id='productMainContentContainerId']/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[1]/div/div[1]/div/div[2]/div/img").element_handle() @@ -732,11 +736,18 @@ class OptionHandler: # 선택자에서 인덱스를 반영해 동적 선택자를 생성 add_button_selector = self.add_button_selector.format(index=index) + # 옵션 박스 내부의 텍스트 확인하여 "제외된 옵션" 포함 여부 검사 + option_text_content = await option_box.inner_text() + if "제외된 옵션" in option_text_content: + self.logger.debug(f"{index}번째 옵션은 제외된 옵션입니다. 번역을 생략합니다.") + continue # 제외된 옵션이므로 다음 옵션으로 이동 + # 옵션 이미지가 존재하는지 확인 option_image = await option_box.query_selector("img") if option_image is None: self.logger.debug(f"{index}번째 옵션에 이미지가 없습니다. 다음 옵션으로 이동합니다.") continue + option_image_url = await option_image.get_attribute("src") self.logger.debug(f"{index}번째 옵션 이미지 URL: {option_image_url}") @@ -780,17 +791,15 @@ class OptionHandler: # 다이알로그 확인 후 삭제 버튼 클릭 try: self.logger.debug(f"{index}번째 옵션의 삭제 다이알로그 확인 중...") - dialog = await self.page.wait_for_selector(".sc-ddjGPC.jbwEYW", timeout=5000) # 다이알로그 클래스 확인 + dialog = await self.page.wait_for_selector(self.delete_dialog_selector, timeout=5000) # 다이알로그 클래스 확인 if dialog: self.logger.debug(f"{index}번째 옵션의 삭제 다이알로그 확인됨") # 삭제 확인 버튼 찾기 - confirm_delete_button = self.page.locator(self.confirm_delete_button_selector) - await confirm_delete_button.wait_for(state="attached", timeout=5000) + confirm_delete_button = await dialog.query_selector(self.confirm_delete_button_selector) - if await confirm_delete_button.is_visible(): - self.logger.debug(f"{index}번째 옵션의 삭제 확인 버튼 확인됨") + if confirm_delete_button: await confirm_delete_button.click() self.logger.debug(f"{index}번째 옵션의 삭제 확인 버튼 클릭됨") else: @@ -806,7 +815,8 @@ class OptionHandler: try: # '+ 버튼' 클릭 후 파일 업로드 self.logger.debug(f"{index}번째 옵션의 이미지추가 버튼 가져오기") - add_button = self.page.locator(add_button_selector) + add_button = await self.page.query_selector(add_button_selector) + if add_button: await add_button.click() diff --git a/whale_translator.py b/whale_translator.py index d41ebfe6..b82b82ef 100644 --- a/whale_translator.py +++ b/whale_translator.py @@ -298,7 +298,7 @@ class WhaleTranslator: self.logger.debug(f"페이지 로딩 완료 후 웨일 창의 가운데로 마우스 커서 이동") # pyautogui.moveTo(960,580) # 마우스 센터로 이동 self.move_mouse_to_center() - time.sleep(1) # 마우스 이동 후 대기 + time.sleep(0.4) # 마우스 이동 후 대기 # original_color = self.get_mouse_position_color() # 현재 색상 가져오기 # self.colors['before'] = original_color @@ -790,7 +790,8 @@ class WhaleTranslator: max_retries (int): 최대 재시도 횟수. """ retry_count = 0 - + self.move_mouse_to_center() + while retry_count < max_retries: self.logger.debug(f"마우스 오른쪽 클릭 시도 #{retry_count + 1}") pyautogui.rightClick()