diff --git a/config.ini b/config.ini
index 3468734a..bdd6d631 100644
--- a/config.ini
+++ b/config.ini
@@ -39,9 +39,39 @@ leading_text_6 = "**반드시 옵션사진과 옵션이름을 확인하시고
leading_text_7 = "---"
# 필요한 만큼 추가 가능
-[ProductNameLocators]
-product_name_input_locator = //*[@id='productMainContentContainerId']/div/div[1]/div/div/div[1]/input
+[TitleLocators]
+# 상품명 관련 선택자
+product_name_input_locator = //*[@id='productMainContentContainerId']/div/div[1]/div[5]/div[1]/span/input
+product_name_input_css_path = 'div#productMainContentContainerId div:nth-child(5) > div:nth-child(1) > span > input'
+
+# 상품명 추천단어 입력칸 선택자
+product_name_suggestion_input_locator = //*[@id="productMainContentContainerId"]/div/div[1]/div[2]/div[2]/div/span/span/span[1]/input
+product_name_suggestion_input_css_path = 'div#productMainContentContainerId div:nth-child(2) > div:nth-child(2) > div > span > span > span.ant-input-affix-wrapper.css-1li46mu.ant-input-outlined > input'
+
+# 상품명 추천단어 입력 검색 버튼 선택자
+product_name_search_button_locator = //*[@id="productMainContentContainerId"]/div/div[1]/div[2]/div[2]/div/span/span/span[2]/button
+product_name_search_button_css_path = 'div#productMainContentContainerId div:nth-child(2) > div:nth-child(2) > div > span > span > span.ant-input-group-addon > button[type="button"]'
+
+# 원본 상품명 선택자
+original_product_name_locator = //*[@id="productMainContentContainerId"]/div/div[1]/div[6]/div[1]/div/span
+original_product_name_css_path = 'div#productMainContentContainerId div.sc-aNeao.tNLFa > div.ant-flex.css-1li46mu.ant-flex-align-stretch.ant-flex-vertical > div:nth-child(1) > div > span'
+
+# 상품명의 경고단어 삭제 버튼 선택자
+product_name_warning_delete_button_locator = //*[@id="productMainContentContainerId"]/div/div[1]/div[6]/div[3]/div[2]/div/button
+product_name_warning_delete_button_css_path = 'div#productMainContentContainerId div:nth-child(2) > div > button[type="button"]'
+
+# 카테고리 관련 선택자
+category_suggestion_button_locator = //*[@id='productMainContentContainerId']/div/div[1]/div[5]/div[2]/button
+category_suggestion_button_css_path = 'div#productMainContentContainerId div:nth-child(2) > button[type="button"]'
+
+# 카테고리 선택자 - 인증 여부에 따른 분기
+category_main_selector_with_cp = '#productMainContentContainerId .ant-select.ant-select-outlined.css-1li46mu.ant-select-single.ant-select-show-arrow:nth-of-type(1)'
+category_main_selector_with_ss = '#productMainContentContainerId .ant-select.ant-select-outlined.css-1li46mu.ant-select-single.ant-select-show-arrow:nth-of-type(2)'
+category_main_selector_with_esm = '#productMainContentContainerId .ant-select.ant-select-outlined.css-1li46mu.ant-select-single.ant-select-show-arrow:nth-of-type(3)'
+category_certified_text_locator = div.ant-col.css-1li46mu:nth-child(1)
+category_text_with_certification_locator = div.ant-col.css-1li46mu:nth-child(2)
+category_text_without_certification_locator = div.ant-col.css-1li46mu:nth-child(1)
[BrowserControl]
# 크롬 창 이름
diff --git a/gui.py b/gui.py
index dafe1090..8b98c44f 100644
--- a/gui.py
+++ b/gui.py
@@ -716,11 +716,11 @@ class TranslationApp(QWidget):
self.logger.debug(f'{index}/{len(product_buttons)}: 세부사항 수정 작업 중...')
- # 상품명 수집 및 수집 오류 처리
- product_name = await self.browser_controller.get_product_name(index, 'css')
- if product_name == "수집 오류 발생":
- self.logger.debug('상품 수집 오류, 다음 상품으로 넘어갑니다.')
- continue
+ # # 상품명 수집 및 수집 오류 처리
+ # product_name = await self.browser_controller.get_product_name(index, 'css')
+ # if product_name == "수집 오류 발생":
+ # self.logger.debug('상품 수집 오류, 다음 상품으로 넘어갑니다.')
+ # continue
# 상품 수정 다이얼로그 열기
await self.browser_controller.open_product_edit_dialog(button)
diff --git a/test/ele_test.py b/test/ele_test.py
new file mode 100644
index 00000000..e51309ce
--- /dev/null
+++ b/test/ele_test.py
@@ -0,0 +1,146 @@
+import asyncio
+from playwright.async_api import async_playwright
+from PySide6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget, QMessageBox
+from qasync import QEventLoop
+import sys
+
+class CategoryHandler:
+ def __init__(self, page):
+ self.page = page
+
+ async def handle_category_action(self):
+ # #productMainContentContainerId 내부에서 클래스 이름 "ant-select ant-select-outlined css-1li46mu ant-select-single ant-select-show-arrow"를 포함한 요소 중 두 번째 요소 찾기
+ print("[DEBUG] handle_category_action: Locating category container element...")
+ category_locator = "div#productMainContentContainerId div.ant-select.ant-select-outlined.css-1li46mu.ant-select-single.ant-select-show-arrow >> nth=1"
+
+ try:
+ await self.page.wait_for_selector(category_locator, timeout=5000)
+ except Exception as e:
+ print(f"[ERROR] handle_category_action: Timed out waiting for category container element. Error: {e}")
+ QMessageBox.information(None, "결과", f"카테고리 컨테이너 요소를 찾는 데 실패했습니다: {e}")
+ return
+
+ category_element = self.page.locator(category_locator)
+ count = await category_element.count()
+ print(f"[DEBUG] handle_category_action: Number of elements found with locator '{category_locator}': {count}")
+ if count == 0:
+ print(f"[ERROR] handle_category_action: Category container element not found using locator '{category_locator}'!")
+ QMessageBox.information(None, "결과", "카테고리 컨테이너 요소를 찾을 수 없습니다.")
+ return
+
+ # "인증필요"와 카테고리 텍스트 추출
+ print("[DEBUG] handle_category_action: Extracting '인증필요' and category text...")
+ certification_text = ""
+ category_text = ""
+
+ try:
+ cert_needed_locator = category_element.locator("div.ant-col.css-1li46mu:nth-child(1)")
+ certification_text = await cert_needed_locator.inner_text()
+ print(f"[DEBUG] handle_category_action: Certification text found - '{certification_text}'")
+ if "인증필요" in certification_text:
+ # 인증필요가 있는 경우 두 번째 요소가 카테고리 텍스트
+ category_text_locator = category_element.locator("div.ant-col.css-1li46mu:nth-child(2)")
+ category_text = await category_text_locator.inner_text()
+ else:
+ # 인증필요가 없는 경우 첫 번째 요소가 카테고리 텍스트
+ category_text = certification_text
+ certification_text = "" # 인증필요가 없으므로 초기화
+ except Exception:
+ # 인증필요가 없는 경우 첫 번째 요소가 카테고리 텍스트
+ print("[DEBUG] handle_category_action: Certification text not found. Assuming first element is category text.")
+ category_text_locator = category_element.locator("div.ant-col.css-1li46mu:nth-child(1)")
+ category_text = await category_text_locator.inner_text()
+
+ full_text = f"{certification_text} {category_text}".strip()
+ print(f"[DEBUG] handle_category_action: Full text - '{full_text}'")
+ QMessageBox.information(None, "검색 결과", f"카테고리 텍스트: {full_text}")
+
+ # 카테고리 텍스트에 '인증'이라는 단어가 포함되어 있는지 검사
+ if "인증" in full_text:
+ print("[INFO] 인증 필요 카테고리입니다. 인증 절차를 진행합니다.")
+ # 인증이 필요한 경우 수행할 작업
+ await self.perform_certification_action()
+ else:
+ print("[INFO] 인증이 필요하지 않은 카테고리입니다.")
+ # 인증이 필요하지 않은 경우 수행할 작업
+ await self.perform_standard_action()
+
+ async def perform_certification_action(self):
+ # 인증 절차를 진행하는 코드 작성
+ print("[DEBUG] perform_certification_action: Starting certification process...")
+ # 예시: 특정 버튼 클릭하기
+ await self.page.click("button:has-text('인증 시작')")
+ print("[INFO] perform_certification_action: Certification process completed.")
+
+ async def perform_standard_action(self):
+ # 인증이 필요하지 않은 경우의 일반적인 작업 코드 작성
+ print("[DEBUG] perform_standard_action: Performing standard action...")
+ # 예시: 다음 단계로 이동
+ await self.page.click("button:has-text('다음 단계')")
+ print("[INFO] perform_standard_action: Standard action completed.")
+
+async def run_playwright():
+ print("[DEBUG] run_playwright: Launching Playwright...")
+ playwright = await async_playwright().start()
+ browser = await playwright.chromium.launch(headless=False)
+ page = await browser.new_page()
+ print("[DEBUG] run_playwright: Navigating to https://www.percenty.co.kr...")
+ await page.goto("https://www.percenty.co.kr") # 실제 페이지 URL로 변경하세요
+ print("[INFO] run_playwright: Page loaded successfully.")
+ return page, browser, playwright
+
+class MainWindow(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.setWindowTitle("Playwright 테스트")
+ self.setGeometry(100, 100, 400, 300)
+ self.page = None
+ self.browser = None
+ self.playwright = None
+
+ # 버튼 생성
+ self.init_button = QPushButton("Playwright 실행")
+ self.init_button.clicked.connect(self.run_playwright_button)
+
+ self.check_button = QPushButton("요소 검사 및 메시지 출력")
+ self.check_button.clicked.connect(self.check_category_button)
+ self.check_button.setEnabled(False)
+
+ # 레이아웃 설정
+ layout = QVBoxLayout()
+ layout.addWidget(self.init_button)
+ layout.addWidget(self.check_button)
+ self.setLayout(layout)
+
+ def run_playwright_button(self):
+ print("[DEBUG] run_playwright_button: Playwright 실행 버튼 클릭됨.")
+ asyncio.create_task(self.init_playwright())
+
+ async def init_playwright(self):
+ print("[DEBUG] init_playwright: Initializing Playwright...")
+ self.page, self.browser, self.playwright = await run_playwright()
+ self.check_button.setEnabled(True)
+ print("[INFO] init_playwright: Playwright initialized and check button enabled.")
+
+ def check_category_button(self):
+ if self.page:
+ print("[DEBUG] check_category_button: 요소 검사 버튼 클릭됨.")
+ asyncio.create_task(self.handle_category_action())
+
+ async def handle_category_action(self):
+ print("[DEBUG] handle_category_action: Handling category action...")
+ handler = CategoryHandler(self.page)
+ await handler.handle_category_action()
+ print("[INFO] handle_category_action: Category check completed.")
+
+if __name__ == "__main__":
+ print("[DEBUG] Main: Starting application...")
+ app = QApplication(sys.argv)
+ loop = QEventLoop(app)
+ asyncio.set_event_loop(loop)
+
+ window = MainWindow()
+ window.show()
+
+ with loop:
+ sys.exit(loop.run_forever())
\ No newline at end of file
diff --git a/title.py b/title.py
new file mode 100644
index 00000000..ec7b2917
--- /dev/null
+++ b/title.py
@@ -0,0 +1,164 @@
+class TitleHandler:
+ """
+ TitleHandler 클래스는 상품명과 카테고리 관련 정보를 처리하는 역할을 합니다.
+ 다양한 선택자를 사용하여 웹 페이지에서 상품명, 추천 단어, 카테고리 등을 수집하고 입력하는 기능을 제공합니다.
+
+ Attributes:
+ page (Page): Playwright의 페이지 객체로, 브라우저와의 상호작용을 담당합니다.
+ logger (Logger): 로깅을 위한 Logger 객체입니다.
+ """
+ def __init__(self, page, logger, locator_manager):
+ self.page = page
+ self.logger = logger
+ self.locator_manager = locator_manager
+
+ # 선택자 로드
+ self.product_name_input_locator = self.locator_manager.get_locator('TitleLocators', 'product_name_input_locator')
+ self.suggestion_input_locator = self.locator_manager.get_locator('TitleLocators', 'suggestion_input_locator')
+ self.search_button_locator = self.locator_manager.get_locator('TitleLocators', 'search_button_locator')
+ self.original_name_locator = self.locator_manager.get_locator('TitleLocators', 'original_name_locator')
+ self.delete_warning_button_locator = self.locator_manager.get_locator('TitleLocators', 'delete_warning_button_locator')
+ self.category_suggestion_button_locator = self.locator_manager.get_locator('TitleLocators', 'category_suggestion_button_locator')
+ self.main_category_locator_with_cp = self.locator_manager.get_locator('TitleLocators', 'category_main_selector_with_cp')
+ self.main_category_locator_with_ss = self.locator_manager.get_locator('TitleLocators', 'category_main_selector_with_ss')
+ self.main_category_locator_with_esm = self.locator_manager.get_locator('TitleLocators', 'category_main_selector_with_esm')
+ self.certified_text_locator = self.locator_manager.get_locator('TitleLocators', 'certified_text_locator')
+ self.category_text_locator = self.locator_manager.get_locator('TitleLocators', 'category_text_locator')
+ self.category_text_locator_certified = self.locator_manager.get_locator('TitleLocators', 'category_text_locator_certified')
+
+ async def get_product_name(self) -> str:
+ """
+ 노출상품명 입력칸에서 상품명을 가져오는 메서드입니다.
+
+ Returns:
+ str: 상품명 텍스트
+ """
+ try:
+ self.logger.debug("노출상품명 입력칸에서 상품명을 가져오는 중입니다.")
+ product_name_element = await self.page.query_selector(self.product_name_input_locator)
+ product_name = await product_name_element.get_attribute('value') if product_name_element else ""
+ self.logger.debug(f"상품명: {product_name}")
+ return product_name
+ except Exception as e:
+ self.logger.error(f"상품명 가져오기 중 오류 발생: {e}", exc_info=True)
+ return ""
+
+ async def enter_product_name_suggestion(self, suggestion: str):
+ """
+ 상품명 추천단어를 입력칸에 입력하는 메서드입니다.
+
+ Args:
+ suggestion (str): 입력할 추천 단어
+ """
+ try:
+ self.logger.debug(f"추천 단어를 상품명 추천 입력칸에 입력 중: {suggestion}")
+ suggestion_input_element = await self.page.query_selector(self.suggestion_input_locator)
+ if suggestion_input_element:
+ await suggestion_input_element.fill(suggestion)
+ self.logger.debug(f"추천 단어 '{suggestion}' 입력 완료.")
+ else:
+ self.logger.error("추천 입력칸 요소를 찾을 수 없습니다.")
+ except Exception as e:
+ self.logger.error(f"추천 입력 단어 입력 중 오류 발생: {e}", exc_info=True)
+
+ async def click_product_name_search_button(self):
+ """
+ 상품명 추천단어 입력칸의 검색 버튼을 클릭하는 메서드입니다.
+ """
+ try:
+ self.logger.debug("상품명 추천단어 검색 버튼 클릭 중.")
+ search_button_element = await self.page.query_selector(self.search_button_locator)
+ if search_button_element:
+ await search_button_element.click()
+ self.logger.debug("검색 버튼 클릭 완료.")
+ else:
+ self.logger.error("검색 버튼 요소를 찾을 수 없습니다.")
+ except Exception as e:
+ self.logger.error(f"상품명 추천 검색 버튼 클릭 중 오류 발생: {e}", exc_info=True)
+
+ async def get_original_product_name(self) -> str:
+ """
+ 원본 상품명을 가져오는 메서드입니다.
+
+ Returns:
+ str: 원본 상품명 텍스트
+ """
+ try:
+ self.logger.debug("원본 상품명을 가져오는 중입니다.")
+ original_name_element = await self.page.query_selector(self.original_name_locator)
+ original_name = await original_name_element.inner_text() if original_name_element else ""
+ self.logger.debug(f"원본 상품명: {original_name}")
+ return original_name
+ except Exception as e:
+ self.logger.error(f"원본 상품명 가져오기 중 오류 발생: {e}", exc_info=True)
+ return ""
+
+ async def delete_warning_word_in_product_name(self):
+ """
+ 상품명에서 경고 단어를 삭제하는 버튼을 클릭하는 메서드입니다.
+ """
+ try:
+ self.logger.debug("경고 단어 삭제 버튼 클릭 중입니다.")
+ delete_button_element = await self.page.query_selector(self.delete_warning_button_locator)
+ if delete_button_element:
+ await delete_button_element.click()
+ self.logger.debug("경고 단어 삭제 버튼 클릭 완료.")
+ else:
+ self.logger.error("경고 단어 삭제 버튼 요소를 찾을 수 없습니다.")
+ except Exception as e:
+ self.logger.error(f"경고 단어 삭제 버튼 클릭 중 오류 발생: {e}", exc_info=True)
+
+ async def click_category_suggestion_button(self):
+ """
+ 카테고리 추천받기 버튼을 클릭하는 메서드입니다.
+ """
+ try:
+ self.logger.debug("카테고리 추천받기 버튼 클릭 중입니다.")
+ category_suggestion_button_element = await self.page.query_selector(self.category_suggestion_button_locator)
+ if category_suggestion_button_element:
+ await category_suggestion_button_element.click()
+ self.logger.debug("카테고리 추천받기 버튼 클릭 완료.")
+ else:
+ self.logger.error("카테고리 추천받기 버튼 요소를 찾을 수 없습니다.")
+ except Exception as e:
+ self.logger.error(f"카테고리 추천받기 버튼 클릭 중 오류 발생: {e}", exc_info=True)
+
+ async def get_category(self, market) -> str:
+ """
+ 카테고리를 가져오는 메서드로 인증 필요 여부에 따라 카테고리 선택자를 다르게 처리합니다.
+
+ Returns:
+ str: 카테고리 텍스트
+ """
+ try:
+ self.logger.debug("카테고리 텍스트를 가져오는 중입니다.")
+ if market == 'ss':
+ main_category_element = await self.page.query_selector(self.main_category_locator_with_ss)
+ self.logger.debug(f"선택 마켓 : 스마트스토어")
+ elif market == 'cp':
+ main_category_element = await self.page.query_selector(self.main_category_locator_with_cp)
+ self.logger.debug(f"선택 마켓 : 쿠팡")
+ elif market == 'esm':
+ main_category_element = await self.page.query_selector(self.main_category_locator_with_esm)
+ self.logger.debug(f"선택 마켓 : ESM")
+ if not main_category_element:
+ self.logger.error("카테고리 메인 선택자를 찾을 수 없습니다.")
+ return ""
+
+ certified_text_element = await main_category_element.query_selector(self.certified_text_locator)
+ if certified_text_element:
+ certified_text = await certified_text_element.inner_text()
+ if "인증" in certified_text:
+ category_text_element = await main_category_element.query_selector(self.category_text_locator_certified)
+ self.logger.debug(f"카테고리 인증 필요 발생: {category_text}")
+ else:
+ category_text_element = certified_text_element
+ category_text = await category_text_element.inner_text() if category_text_element else ""
+ self.logger.debug(f"카테고리 텍스트: {category_text}")
+ return category_text
+ else:
+ self.logger.error("카테고리 인증 요소를 찾을 수 없습니다.")
+ return ""
+ except Exception as e:
+ self.logger.error(f"카테고리 텍스트 가져오기 중 오류 발생: {e}", exc_info=True)
+ return ""
\ No newline at end of file
diff --git a/title.txt b/title.txt
new file mode 100644
index 00000000..db6f6ff6
--- /dev/null
+++ b/title.txt
@@ -0,0 +1,47 @@
+상품명 / 카테고리
+
+상품명 추천단어 입력칸의 요소
+
+상품명 추천단어 입력칸의 css
+#productMainContentContainerId > div > div.sc-aNeao.tNLFa > div:nth-child(2) > div:nth-child(2) > div > span > span > span.ant-input-affix-wrapper.css-1li46mu.ant-input-outlined > input
+css path
+"div#productMainContentContainerId div:nth-child(2) > div:nth-child(2) > div > span > span > span.ant-input-affix-wrapper.css-1li46mu.ant-input-outlined > input"
+xpath
+//*[@id="productMainContentContainerId"]/div/div[1]/div[2]/div[2]/div/span/span/span[1]/input
+
+상품명 추천단어 입력 검색버튼 요소
+
+상품명 추천단어 입력 검색버튼 css
+#productMainContentContainerId > div > div.sc-aNeao.tNLFa > div:nth-child(2) > div:nth-child(2) > div > span > span > span.ant-input-group-addon > button
+상품명 추천단어 입력 검색버튼 css path
+"div#productMainContentContainerId div:nth-child(2) > div:nth-child(2) > div > span > span > span.ant-input-group-addon > button[type=\"button\"]"
+상품명 추천단어 입력 검색버튼 xpath
+//*[@id="productMainContentContainerId"]/div/div[1]/div[2]/div[2]/div/span/span/span[2]/button
+
+카테고리 추천받기 버튼 요소
+
+카테고리 추천받기 버튼 css
+#productMainContentContainerId > div > div.sc-aNeao.tNLFa > div:nth-child(5) > div:nth-child(2) > button
+카테고리 추천받기 버튼 css path
+"div#productMainContentContainerId div:nth-child(2) > button[type=\"button\"]"
+카테고리 추천받기 버튼 xpath
+//*[@id="productMainContentContainerId"]/div/div[1]/div[5]/div[2]/button
+
+
+
+노출상품명 입력칸
+//*[@id="productMainContentContainerId"]/div/div[1]/div[5]/div[1]/span/input
+"div#productMainContentContainerId div:nth-child(5) > div:nth-child(1) > span > input"
+
+
+원본상품명
+//*[@id="productMainContentContainerId"]/div/div[1]/div[6]/div[1]/div/span
+"div#productMainContentContainerId div.sc-aNeao.tNLFa > div.ant-flex.css-1li46mu.ant-flex-align-stretch.ant-flex-vertical > div:nth-child(1) > div > span"
+回力联名玉桂狗女童鞋2024春秋季限量卡通款小孩儿童运动舒适板鞋
+
+상품명의 경고단어 삭제 버튼
+//*[@id="productMainContentContainerId"]/div/div[1]/div[6]/div[3]/div[2]/div/button
+"div#productMainContentContainerId div:nth-child(2) > div > button[type=\"button\"]"
+
+
+