717 lines
36 KiB
Python
717 lines
36 KiB
Python
import re
|
|
from typing import List
|
|
from datetime import datetime
|
|
import random
|
|
from collections import OrderedDict
|
|
from translatepy import Translator # 번역 라이브러리 예시
|
|
from titleManager.gpt_client import GPTClient
|
|
from titleManager.mongoDBManager import MongoDBManager
|
|
from titleManager.forbiddenWD_Manager import ForbiddenWordManager
|
|
from titleManager.naver_parser import NaverParser
|
|
from titleManager.naverAPI import NaverSearchAPI
|
|
from titleManager.kiprisAPI import Kipris_API
|
|
import logging
|
|
|
|
class TitleGenerator:
|
|
def __init__(self, locator_manager, browser_controller, logger, toggle_states, gpt_client):
|
|
self.logger = logger
|
|
self.browser_controller = browser_controller
|
|
self.page = self.browser_controller.page
|
|
self.locator_manager = locator_manager
|
|
self.toggle_states = toggle_states
|
|
|
|
self.db_manager = MongoDBManager(db_url='mongodb://root:1234@cckb9998.synology.me:27017/')
|
|
self.forbidden_word_manager = ForbiddenWordManager(self.db_manager)
|
|
self.naver_parser = NaverParser()
|
|
client_id = toggle_states.get("clientID", None)
|
|
client_secret = toggle_states.get("clientSecret", None)
|
|
# client_id = toggle_states["clientID"]
|
|
# client_secret = toggle_states["clientSecret"]
|
|
|
|
# client_id = Uq5c9J_WdQYF8e2wOQT4
|
|
# client_secret = y0CnrADAae
|
|
|
|
self.naverAPI = NaverSearchAPI(client_id=client_id, client_secret=client_secret, logger=self.logger)
|
|
self.kipris_api = Kipris_API(logger, apikey='X9Tz3JqC/JcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6+t3dyLZV3gh2TPXdNhxsRQwaKP673Q==')
|
|
# self.gpt_client = GPTClient(logger, api_key="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")
|
|
self.gpt_client = gpt_client
|
|
self.translator = Translator() # 번역 라이브러리 초기화
|
|
|
|
# 선택자 로드
|
|
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_product_name_locator = self.locator_manager.get_locator('TitleLocators', 'original_product_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.category_main_selector_with_cp = self.locator_manager.get_locator('TitleLocators', 'category_main_selector_with_cp')
|
|
self.category_main_selector_with_ss = self.locator_manager.get_locator('TitleLocators', 'category_main_selector_with_ss')
|
|
self.category_main_selector_with_esm = self.locator_manager.get_locator('TitleLocators', 'category_main_selector_with_esm')
|
|
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')
|
|
|
|
# self.title_infos = {
|
|
# "original_name": None,
|
|
# "keyword_name": None,
|
|
# "generated_name": None,
|
|
# "category": None,
|
|
# }
|
|
|
|
self.initial_title_infos()
|
|
|
|
def initial_title_infos(self):
|
|
self.title_infos = {
|
|
"original_name": None,
|
|
"keyword_name": None,
|
|
"generated_name": None,
|
|
"category": None,
|
|
"category_ss": None,
|
|
"category_esm": None,
|
|
"is_group_ESM": False,
|
|
"is_certified_group_SS": False,
|
|
"search_result": {}, # 추가된 필드
|
|
"top_5_titles": [], # 추가된 필드
|
|
"top_5_prices": [], # 추가된 필드
|
|
"keyword_tags": None, # 추가된 필드
|
|
"top_product_prices": None, # 추가된 필드
|
|
}
|
|
|
|
def update_page(self, page1):
|
|
self.page = page1
|
|
self.logger.log(f"page객체 업데이트 : {page1}", level=logging.DEBUG)
|
|
|
|
def translate_product_name(self, original_name: str) -> str:
|
|
"""텍스트를 한국어로 번역하는 메서드"""
|
|
try:
|
|
result = self.translator.translate(original_name, "ko")
|
|
return result.result if result else original_name
|
|
except Exception as e:
|
|
self.logger.log(f"번역 중 오류 발생: {e}", level=logging.DEBUG)
|
|
return original_name
|
|
|
|
def is_word_forbidden(self, word: str) -> bool:
|
|
"""금지어 매니저를 통해 단어가 금지어인지 확인"""
|
|
return self.forbidden_word_manager.is_word_forbidden(word)
|
|
|
|
def search_trademark(self, word: str) -> dict:
|
|
"""키프리스 API로 단어를 검색하는 메서드"""
|
|
return self.kipris_api.search_trademark(word)
|
|
|
|
def is_valid_word(self, word: str) -> bool:
|
|
"""숫자로만 이루어진 단어 또는 영어와 숫자로만 이루어진 단어를 검증하는 함수."""
|
|
return not (word.isdigit() or re.fullmatch(r'[A-Za-z0-9]+', word))
|
|
|
|
def extract_special_words(self, original_name: str) -> list:
|
|
"""원본 상품명에서 숫자로만 이루어진 단어와 영어와 숫자로만 이루어진 단어를 추출하는 함수."""
|
|
return [word for word in original_name.split() if word.isdigit() or re.fullmatch(r'[A-Za-z0-9]+', word)]
|
|
|
|
def filter_invalid_words(self, words: list) -> list:
|
|
"""영어만 이루어진 단어와 영어와 숫자로 이루어진 단어를 제외하는 함수."""
|
|
return [word for word in words if not re.fullmatch(r'[A-Za-z0-9]+', word)]
|
|
|
|
def process_top_titles(self, top_titles: list) -> list:
|
|
"""top_titles에서 유효하지 않은 단어(영어만 이루어진 단어와 영어와 숫자로 이루어진 단어)를 제외하는 함수."""
|
|
filtered_titles = []
|
|
for title in top_titles:
|
|
filtered_words = self.filter_invalid_words(title.split())
|
|
filtered_titles.append(' '.join(filtered_words))
|
|
return filtered_titles
|
|
|
|
def get_search_result_to_crawling(self, keyword_name):
|
|
try:
|
|
# 키워드 상품명으로 검색
|
|
search_result = self.naver_parser.search_and_parse(" ".join(keyword_name.split()[:4]))
|
|
self.logger.log(f"naver_parser search_result : {search_result}", level=logging.DEBUG)
|
|
|
|
# 데이터 변환
|
|
top_products = search_result.get("top_products", [])
|
|
related_tags = search_result.get("related_tags", [])
|
|
|
|
# 변환된 결과 리스트 생성
|
|
result = []
|
|
for product in top_products:
|
|
result.append({
|
|
"title": product.get("title", ""),
|
|
"price": int(product.get("price", "0").replace(",", "")),
|
|
"delivery_fee": int(product.get("delivery_fee_content", "0").replace(",", "")),
|
|
"purchase_count": int(product.get("purchase_count", "")),
|
|
"rank": int(product.get("rank", "")),
|
|
"category": product.get("category", []),
|
|
"manu_tag": product.get("manu_tag", ""),
|
|
})
|
|
|
|
# 태그 추가
|
|
result.append({"related_tags": related_tags})
|
|
|
|
return result
|
|
except Exception as e:
|
|
self.logger.log(f"상품명 검색 중 오류 발생: {e}", level=logging.ERROR)
|
|
return []
|
|
|
|
def get_search_result_to_API(self, keyword_name):
|
|
try:
|
|
# 키워드 상품명으로 API 검색
|
|
search_result = self.naverAPI.search(keyword_name)
|
|
self.logger.log(f"naverAPI search_result : {search_result}", level=logging.DEBUG)
|
|
|
|
# 데이터 변환
|
|
parsed_products = self.naverAPI.parse_search_results_list(search_result) # 리스트 반환
|
|
result = []
|
|
for product in parsed_products:
|
|
result.append({
|
|
"title": product.get("title", ""),
|
|
"price": int(product.get("price", "0").replace(",", "")),
|
|
"category": product.get("category", []),
|
|
"manu_tag": product.get("manu_tag", ""),
|
|
})
|
|
|
|
# 태그는 없으므로 빈 리스트 추가
|
|
result.append({"related_tags": []})
|
|
|
|
return result
|
|
except Exception as e:
|
|
self.logger.log(f"API 검색 중 오류 발생: {e}", level=logging.ERROR)
|
|
return []
|
|
|
|
|
|
def process_tags(self, tags: List[str]) -> List[str]:
|
|
"""태그 리스트를 정리하는 메서드."""
|
|
forbidden_words = {"오늘발송", "오늘출발", "당일배송"} # 제거할 키워드
|
|
unique_tags = list(set(tags)) # 중복 제거
|
|
filtered_tags = [tag for tag in unique_tags if tag not in forbidden_words] # 금지어 필터링
|
|
return filtered_tags
|
|
|
|
def generate_product_title(self, original_name: str, keyword_name: str, search_result: dict, product_category: str) -> str:
|
|
"""상품명을 생성하는 메서드"""
|
|
# 1. 원본 상품명 번역 및 관련성 판단
|
|
translated_name = self.translate_product_name(original_name)
|
|
self.logger.log(f'translated_name : {translated_name}', level=logging.DEBUG)
|
|
is_related = self.gpt_client.is_related_product(translated_name, keyword_name)
|
|
|
|
if not is_related:
|
|
self.logger.log(f'원본상품명과 쇼핑렌즈로 상품명 생성로직 필요.', level=logging.DEBUG)
|
|
|
|
# 2. 검색 결과에서 제목과 가격 가져오기
|
|
top_titles = []
|
|
top_prices = []
|
|
|
|
if search_result:
|
|
# search_result 리스트 처리
|
|
for product in search_result:
|
|
if "title" in product and "price" in product:
|
|
top_titles.append(product["title"])
|
|
top_prices.append(product["price"])
|
|
|
|
self.logger.log(f'top_titles : {top_titles}', level=logging.DEBUG)
|
|
self.logger.log(f'top_prices : {top_prices}', level=logging.DEBUG)
|
|
else:
|
|
self.logger.log("검색 결과가 비어 있습니다.", level=logging.WARNING)
|
|
|
|
# 3. 검색된 제목 필터링
|
|
filtered_top_titles = self.process_top_titles(top_titles)
|
|
|
|
|
|
# 3. 키워드 상품명에서 첫 4개 키워드 추출
|
|
essential_keywords = keyword_name.split()[:4]
|
|
self.logger.log(f'essential_keywords (첫 4개 키워드): {essential_keywords}', level=logging.DEBUG)
|
|
|
|
keyword_title = list(set(
|
|
word for title in [keyword_name] + filtered_top_titles
|
|
for word in title.split()
|
|
))
|
|
self.logger.log(f'keyword_title : {keyword_title}', level=logging.DEBUG)
|
|
|
|
# 3. 숫자나 영어와 숫자로만 이루어진 단어 필터링
|
|
keyword_title = [word for word in keyword_title if self.is_valid_word(word)]
|
|
self.logger.log(f'keyword_title after filtering invalid words : {keyword_title}', level=logging.DEBUG)
|
|
|
|
# 4. 중복단어 제거
|
|
keyword_title = list(set(keyword_title)) # 중복 제거
|
|
self.logger.log(f'final keyword_title after KIPRIS search: {keyword_title}', level=logging.DEBUG)
|
|
|
|
# 5. 원본 상품명에서 숫자 또는 영어와 숫자로만 이루어진 단어 추출 및 포함
|
|
special_words = self.extract_special_words(original_name)
|
|
self.logger.log(f'special_words from original_name: {special_words}', level=logging.DEBUG)
|
|
keyword_title.extend(special_words)
|
|
|
|
# 6. 중복 제거 후 최종 리스트 생성
|
|
keyword_title = list(set(keyword_title))
|
|
self.logger.log(f'final keyword_title including special words: {keyword_title}', level=logging.DEBUG)
|
|
|
|
# 7. 필수 키워드에서 랜덤으로 2개 선택
|
|
required_keywords = random.sample(essential_keywords, min(2, len(essential_keywords)))
|
|
self.logger.log(f'randomly selected required_keywords: {required_keywords}', level=logging.DEBUG)
|
|
|
|
# 필수 키워드 병합
|
|
keyword_title.extend(required_keywords)
|
|
keyword_title = list(set(keyword_title)) # 중복 제거
|
|
|
|
# 4. 금지어 필터링
|
|
keyword_title = [word for word in keyword_title if not self.is_word_forbidden(word)]
|
|
self.logger.log(f'keyword_title after forbidden filter : {keyword_title}', level=logging.DEBUG)
|
|
|
|
# 9. 최종 상품명 생성
|
|
product_title = self.gpt_client.generate_product_name_next(words=keyword_title, original_name=original_name, top_titles=top_titles)
|
|
self.logger.log(f'final product_title: {product_title}', level=logging.DEBUG)
|
|
|
|
return product_title
|
|
|
|
|
|
async def get_initial_info(self, use_api: bool = False):
|
|
try:
|
|
# 1. 딕셔너리 초기화
|
|
self.initial_title_infos()
|
|
|
|
# 2. 해당상품 정보 추출
|
|
original_name = await self.get_original_product_name()
|
|
self.logger.log(f'original_name: {original_name}', level=logging.DEBUG)
|
|
self.title_infos["original_name"] = original_name
|
|
|
|
keyword_name = await self.get_product_name()
|
|
self.logger.log(f'keyword_name: {keyword_name}', level=logging.DEBUG)
|
|
self.title_infos["keyword_name"] = keyword_name
|
|
|
|
# product_category = await self.get_category(market='ss') # 카테고리 가져오기
|
|
# self.logger.log(f'product_category: {product_category}', level=logging.DEBUG)
|
|
# self.title_infos["category"] = product_category
|
|
|
|
product_category_data = await self.get_category() # ss와 esm에서 카테고리 정보 가져오기
|
|
# ss 마켓 카테고리 처리
|
|
product_category_ss = product_category_data.get("category_text_ss", "")
|
|
is_certified_ss = product_category_data.get("is_certified_ss", False)
|
|
# esm 마켓 카테고리 처리
|
|
product_category_esm = product_category_data.get("category_text_esm", "")
|
|
is_group_esm = product_category_data.get("is_group_esm", False)
|
|
|
|
# 로깅
|
|
self.logger.log(f'SS 마켓 카테고리: {product_category_ss}', level=logging.DEBUG)
|
|
self.logger.log(f'SS 마켓 인증 필요 여부: {is_certified_ss}', level=logging.DEBUG)
|
|
self.logger.log(f'ESM 마켓 카테고리: {product_category_esm}', level=logging.DEBUG)
|
|
self.logger.log(f'ESM 마켓 그룹상품 여부: {is_group_esm}', level=logging.DEBUG)
|
|
|
|
# title_infos에 저장
|
|
self.title_infos["category"] = product_category_ss
|
|
self.title_infos["category_ss"] = product_category_ss
|
|
self.title_infos["category_esm"] = product_category_esm
|
|
self.title_infos["is_certified_group_SS"] = is_certified_ss
|
|
self.title_infos["is_group_ESM"] = is_group_esm
|
|
|
|
|
|
# 3. 검색 결과 가져오기 (crawling 또는 API 선택)
|
|
search_result = None
|
|
if not use_api:
|
|
# 기본적으로 크롤링 시도
|
|
self.logger.log("크롤링을 통해 검색 결과 가져오는 중...", level=logging.INFO)
|
|
try:
|
|
search_result = self.get_search_result_to_crawling(self.title_infos["keyword_name"])
|
|
self.logger.log(f'크롤링 검색 결과: {search_result}', level=logging.DEBUG)
|
|
except Exception as e:
|
|
self.logger.log(f"크롤링 실패: {e}. API로 전환합니다.", level=logging.WARNING)
|
|
use_api = True # 크롤링 실패 시 API로 전환
|
|
|
|
if use_api:
|
|
# API 방식으로 처리
|
|
self.logger.log("API를 통해 검색 결과 가져오는 중...", level=logging.INFO)
|
|
try:
|
|
search_result = self.get_search_result_to_API(self.title_infos["keyword_name"])
|
|
self.logger.log(f'API 검색 결과: {search_result}', level=logging.DEBUG)
|
|
except Exception as e:
|
|
self.logger.log(f"API 검색 실패: {e}", level=logging.ERROR)
|
|
return {} # API 실패 시 빈 값 반환
|
|
|
|
if not search_result:
|
|
self.logger.log("검색 결과를 찾을 수 없습니다.", level=logging.WARNING)
|
|
return {} # 검색 결과가 없을 경우 빈 값 반환
|
|
|
|
self.title_infos["search_result"] = search_result
|
|
|
|
# 4. 검색 결과 처리
|
|
related_tags = search_result[-1].get("related_tags", []) # 마지막 요소에서 태그 추출
|
|
products = search_result[:-1] # 마지막 요소 제외
|
|
|
|
# 상위 제품 정보 저장
|
|
self.title_infos["top_5_titles"] = [p.get("title", "") for p in products[:5]]
|
|
self.title_infos["top_5_prices"] = [p.get("price", 0) for p in products[:5]]
|
|
|
|
self.logger.log(f'제목 리스트: {self.title_infos["top_5_titles"]}', level=logging.DEBUG)
|
|
self.logger.log(f'가격 리스트: {self.title_infos["top_5_prices"]}', level=logging.DEBUG)
|
|
|
|
# 제조사 태그(manu_tags) 수집
|
|
# 5. 제조사 태그(manu_tags) 수집
|
|
manu_tags = [product["manu_tag"] for product in products[:5] if "manu_tag" in product and product["manu_tag"]]
|
|
self.logger.log(f"추출된 manu_tags: {manu_tags}", level=logging.DEBUG)
|
|
|
|
# 관련 태그와 제조사 태그를 결합하여 중복 제거
|
|
if related_tags or manu_tags:
|
|
combined_tags = list(set(related_tags + manu_tags)) # 중복 제거
|
|
processed_tags = self.process_tags(combined_tags)
|
|
self.logger.log(f"처리된 태그: {processed_tags}", level=logging.DEBUG)
|
|
self.title_infos["keyword_tags"] = processed_tags
|
|
else:
|
|
self.logger.log("관련 태그나 제조사 태그를 찾을 수 없습니다.", level=logging.WARNING)
|
|
self.title_infos["keyword_tags"] = []
|
|
|
|
return self.title_infos
|
|
|
|
except Exception as e:
|
|
self.logger.log(f"초기 상품정보 생성 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
return {}
|
|
|
|
async def process_title(self):
|
|
try:
|
|
# # 1. 딕셔너리 초기화
|
|
# self.initial_title_infos()
|
|
|
|
# # 2. 해당상품 정보 추출
|
|
# original_name = await self.get_original_product_name()
|
|
# keyword_name = await self.get_product_name()
|
|
# product_category = await self.get_category(market='ss') # 카테고리 가져오기
|
|
|
|
# # 3. 검색결과 가져오기
|
|
# search_result = self.get_search_result(["keyword_name"])
|
|
|
|
# # 4. 검색결과에서 태그 추출
|
|
# related_tags = search_result.get("related_tags", [])
|
|
# manu_tags = [product.get("manu_tag") for product in search_result.get("top_products", []) if product.get("manu_tag")]
|
|
|
|
# combined_tags = list(set(related_tags + manu_tags))
|
|
# processed_tags = self.process_tags(combined_tags)
|
|
|
|
# # 5. price 추출
|
|
# # top_product_prices = [product.get("price") for product in search_result.get("top_products", []) if product.get("price")]
|
|
# top_product_prices = [
|
|
# int(product.get("price").replace(",", ""))
|
|
# for product in search_result.get("top_products", [])
|
|
# if product.get("price")
|
|
# ]
|
|
|
|
# 6. 상품명 생성
|
|
product_title = self.generate_product_title(original_name=self.title_infos["original_name"], keyword_name=self.title_infos["keyword_name"], search_result=self.title_infos["search_result"], product_category=self.title_infos["category"])
|
|
if product_title == "관련성이 없는 상품 - 체크필요":
|
|
return
|
|
|
|
# 7. 결과 저장
|
|
# self.title_infos["original_name"] = original_name
|
|
# self.title_infos["keyword_name"] = keyword_name
|
|
# self.title_infos["category"] = product_category
|
|
|
|
self.title_infos["generated_name"] = product_title
|
|
# self.title_infos["keyword_tags"] = processed_tags
|
|
# self.title_infos["top_product_prices"] = top_product_prices
|
|
|
|
# 8. 상품명 설정
|
|
is_success = await self.set_product_name(product_title)
|
|
|
|
if is_success:
|
|
self.logger.log("상품명 생성 및 설정 성공", level=logging.INFO)
|
|
else:
|
|
self.logger.log("상품명 설정 실패", level=logging.WARNING)
|
|
|
|
self.logger.log(f"Title_Infos : {self.title_infos}", level=logging.DEBUG)
|
|
|
|
return product_title
|
|
|
|
except Exception as e:
|
|
self.logger.log(f"상품명 생성 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
|
|
|
|
async def process_title_ori(self):
|
|
try:
|
|
original_name = await self.get_original_product_name()
|
|
keyword_name = await self.get_product_name()
|
|
product_category = await self.get_category(market='ss') # 카테고리 가져오기
|
|
|
|
product_title = self.generate_product_title(original_name=original_name, keyword_name=keyword_name)
|
|
|
|
# 결과 저장
|
|
self.title_infos["original_name"] = original_name
|
|
self.title_infos["keyword_name"] = keyword_name
|
|
self.title_infos["generated_name"] = product_title
|
|
self.title_infos["category"] = product_category
|
|
|
|
# 상품명 설정
|
|
is_success = await self.set_product_name(product_title)
|
|
|
|
if is_success:
|
|
self.logger.log("상품명 생성 및 설정 성공", level=logging.INFO)
|
|
else:
|
|
self.logger.log("상품명 설정 실패", level=logging.WARNING)
|
|
|
|
self.logger.log(f"Title_Infos : {self.title_infos}", level=logging.DEBUG)
|
|
|
|
return self.title_infos
|
|
|
|
except Exception as e:
|
|
self.logger.log(f"상품명 생성 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
|
|
async def get_product_name(self) -> str:
|
|
"""
|
|
노출상품명 입력칸에서 상품명을 가져오는 메서드입니다.
|
|
|
|
Returns:
|
|
str: 상품명 텍스트
|
|
"""
|
|
try:
|
|
self.logger.log("노출상품명 입력칸에서 상품명을 가져오는 중입니다.", level=logging.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.log(f"상품명: {product_name}", level=logging.DEBUG)
|
|
return product_name
|
|
except Exception as e:
|
|
self.logger.log(f"상품명 가져오기 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
return ""
|
|
|
|
async def set_product_name(self, product_name: str) -> bool:
|
|
"""
|
|
노출상품명 입력칸에 상품명을 설정하는 메서드입니다.
|
|
|
|
Args:
|
|
product_name (str): 설정할 상품명
|
|
|
|
Returns:
|
|
bool: 성공 여부 (True: 성공, False: 실패)
|
|
"""
|
|
try:
|
|
self.logger.log(f"노출상품명 입력칸에 '{product_name}' 설정 중입니다.", level=logging.DEBUG)
|
|
product_name_element = await self.page.query_selector(self.product_name_input_locator)
|
|
|
|
if product_name_element:
|
|
await product_name_element.fill(product_name)
|
|
self.logger.log(f"상품명 '{product_name}'이 성공적으로 입력되었습니다.", level=logging.DEBUG)
|
|
return True
|
|
else:
|
|
self.logger.log("상품명 입력칸을 찾을 수 없습니다.", level=logging.ERROR)
|
|
return False
|
|
except Exception as e:
|
|
self.logger.log(f"상품명 설정 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
return False
|
|
|
|
async def enter_product_name_suggestion(self, suggestion: str):
|
|
"""
|
|
상품명 추천단어를 입력칸에 입력하는 메서드입니다.
|
|
|
|
Args:
|
|
suggestion (str): 입력할 추천 단어
|
|
"""
|
|
try:
|
|
self.logger.log(f"추천 단어를 상품명 추천 입력칸에 입력 중: {suggestion}", level=logging.DEBUG)
|
|
suggestion_input_element = await self.page.query_selector(self.suggestion_input_locator)
|
|
if suggestion_input_element:
|
|
await suggestion_input_element.fill(suggestion)
|
|
self.logger.log(f"추천 단어 '{suggestion}' 입력 완료.", level=logging.DEBUG)
|
|
else:
|
|
self.logger.log("추천 입력칸 요소를 찾을 수 없습니다.", level=logging.ERROR)
|
|
except Exception as e:
|
|
self.logger.log(f"추천 입력 단어 입력 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
|
|
async def click_product_name_search_button(self):
|
|
"""
|
|
상품명 추천단어 입력칸의 검색 버튼을 클릭하는 메서드입니다.
|
|
"""
|
|
try:
|
|
self.logger.log("상품명 추천단어 검색 버튼 클릭 중.", level=logging.DEBUG)
|
|
search_button_element = await self.page.query_selector(self.search_button_locator)
|
|
if search_button_element:
|
|
await search_button_element.click()
|
|
self.logger.log("검색 버튼 클릭 완료.", level=logging.DEBUG)
|
|
else:
|
|
self.logger.log("검색 버튼 요소를 찾을 수 없습니다.", level=logging.ERROR)
|
|
except Exception as e:
|
|
self.logger.log(f"상품명 추천 검색 버튼 클릭 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
|
|
async def get_original_product_name(self) -> str:
|
|
"""
|
|
원본 상품명을 가져오는 메서드입니다.
|
|
|
|
Returns:
|
|
str: 원본 상품명 텍스트
|
|
"""
|
|
try:
|
|
self.logger.log("원본 상품명을 가져오는 중입니다.", level=logging.DEBUG)
|
|
original_name_element = await self.page.query_selector(self.original_product_name_locator)
|
|
original_name = await original_name_element.inner_text() if original_name_element else ""
|
|
self.logger.log(f"원본 상품명: {original_name}", level=logging.DEBUG)
|
|
return original_name
|
|
except Exception as e:
|
|
self.logger.log(f"원본 상품명 가져오기 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
return ""
|
|
|
|
async def delete_warning_word_in_product_name(self):
|
|
"""
|
|
상품명에서 경고 단어를 삭제하는 버튼을 클릭하는 메서드입니다.
|
|
"""
|
|
try:
|
|
self.logger.log("경고 단어 삭제 버튼 클릭 중입니다.", level=logging.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.log("경고 단어 삭제 버튼 클릭 완료.", level=logging.DEBUG)
|
|
else:
|
|
self.logger.log("경고 단어 삭제 버튼 요소를 찾을 수 없습니다.", level=logging.ERROR)
|
|
except Exception as e:
|
|
self.logger.log(f"경고 단어 삭제 버튼 클릭 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
|
|
async def click_category_suggestion_button(self):
|
|
"""
|
|
카테고리 추천받기 버튼을 클릭하는 메서드입니다.
|
|
"""
|
|
try:
|
|
self.logger.log("카테고리 추천받기 버튼 클릭 중입니다.", level=logging.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.log("카테고리 추천받기 버튼 클릭 완료.", level=logging.DEBUG)
|
|
else:
|
|
self.logger.log("카테고리 추천받기 버튼 요소를 찾을 수 없습니다.", level=logging.ERROR)
|
|
except Exception as e:
|
|
self.logger.log(f"카테고리 추천받기 버튼 클릭 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
|
|
async def get_category_ori(self, market='ss') -> str:
|
|
"""
|
|
카테고리를 가져오는 메서드로 인증 필요 여부에 따라 카테고리 선택자를 다르게 처리합니다.
|
|
|
|
Returns:
|
|
str: 카테고리 텍스트
|
|
"""
|
|
try:
|
|
self.logger.log(f"마켓 : {market} - 카테고리 텍스트를 가져오는 중입니다.", level=logging.DEBUG)
|
|
|
|
if market == 'ss':
|
|
category_locator = self.category_main_selector_with_ss
|
|
elif market == 'cp':
|
|
category_locator = self.category_main_selector_with_cp
|
|
elif market == 'esm':
|
|
category_locator = self.category_main_selector_with_esm
|
|
|
|
self.logger.log(f"category_locator : {category_locator}", level=logging.DEBUG)
|
|
|
|
await self.page.wait_for_selector(category_locator, timeout=5000, state="attached") # 요소가 나타날 때까지 대기
|
|
main_category_element = self.page.locator(category_locator) # 대기 후 동기적으로 요소 가져오기
|
|
self.logger.log(f"main_category_element : {main_category_element}", level=logging.DEBUG)
|
|
|
|
if not await main_category_element.count():
|
|
self.logger.log("카테고리 메인 선택자를 찾을 수 없습니다.", level=logging.ERROR)
|
|
return ""
|
|
|
|
# 인증 텍스트 요소 선택
|
|
category_text_element = main_category_element.locator(self.category_text_locator)
|
|
|
|
self.logger.log(f"category_text_element : {category_text_element}", level=logging.DEBUG)
|
|
|
|
if await category_text_element.count():
|
|
category_text = await category_text_element.inner_text()
|
|
|
|
if "인증" in category_text:
|
|
self.logger.log(f"카테고리 인증 필요 발생 category_text = {category_text}", level=logging.DEBUG)
|
|
category_text_certified_element = main_category_element.locator(self.category_text_locator_certified)
|
|
|
|
if await category_text_certified_element.count():
|
|
category_text = await category_text_certified_element.inner_text()
|
|
self.logger.log(f"인증 필요 카테고리 text = {category_text}", level=logging.DEBUG)
|
|
if "그룹상품" in category_text:
|
|
self.logger.log(f"카테고리 그룹상품 발생 category_text = {category_text}", level=logging.DEBUG)
|
|
category_text_certified_element = main_category_element.locator(self.category_text_locator_certified)
|
|
|
|
if await category_text_certified_element.count():
|
|
category_text = await category_text_certified_element.inner_text()
|
|
self.logger.log(f"그룹상품 카테고리 text = {category_text}", level=logging.DEBUG)
|
|
else:
|
|
self.logger.log(f"카테고리 text = {category_text}", level=logging.DEBUG)
|
|
|
|
return category_text
|
|
|
|
else:
|
|
self.logger.log("카테고리 인증 요소를 찾을 수 없습니다.", level=logging.ERROR)
|
|
return ""
|
|
|
|
except Exception as e:
|
|
self.logger.log(f"카테고리 텍스트 가져오기 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
return ""
|
|
|
|
|
|
|
|
async def get_category(self) -> dict:
|
|
"""
|
|
카테고리를 가져오는 메서드로 esm과 ss 마켓에서 카테고리를 수집하고
|
|
인증 및 그룹상품 정보를 처리합니다.
|
|
|
|
Returns:
|
|
dict: 카테고리 정보와 인증 및 그룹상품 여부를 포함한 딕셔너리
|
|
"""
|
|
try:
|
|
category_data = {
|
|
"category_text_ss": "",
|
|
"category_text_esm": "",
|
|
"is_certified_ss": False,
|
|
"is_group_esm": False,
|
|
}
|
|
|
|
for market in ['ss', 'esm']:
|
|
self.logger.log(f"마켓: {market} - 카테고리 텍스트를 가져오는 중입니다.", level=logging.DEBUG)
|
|
|
|
if market == 'ss':
|
|
category_locator = self.category_main_selector_with_ss
|
|
elif market == 'esm':
|
|
category_locator = self.category_main_selector_with_esm
|
|
else:
|
|
continue
|
|
|
|
self.logger.log(f"{market}의 category_locator: {category_locator}", level=logging.DEBUG)
|
|
|
|
# 선택자 대기 및 요소 가져오기
|
|
await self.page.wait_for_selector(category_locator, timeout=5000, state="attached")
|
|
main_category_element = self.page.locator(category_locator)
|
|
self.logger.log(f"{market}의 main_category_element: {main_category_element}", level=logging.DEBUG)
|
|
|
|
if not await main_category_element.count():
|
|
self.logger.log(f"{market}의 카테고리 메인 선택자를 찾을 수 없습니다.", level=logging.ERROR)
|
|
continue
|
|
|
|
# 인증 텍스트 요소 선택
|
|
category_text_element = main_category_element.locator(self.category_text_locator)
|
|
if await category_text_element.count():
|
|
category_text = await category_text_element.inner_text()
|
|
self.logger.log(f"{market}의 category_text: {category_text}", level=logging.DEBUG)
|
|
|
|
# 인증 처리
|
|
if "인증" in category_text:
|
|
self.logger.log(f"{market}에서 인증 카테고리 발생: {category_text}", level=logging.DEBUG)
|
|
certified_element = main_category_element.locator(self.category_text_locator_certified)
|
|
if await certified_element.count():
|
|
category_text = await certified_element.inner_text()
|
|
self.logger.log(f"{market}에서 인증된 카테고리 텍스트: {category_text}", level=logging.DEBUG)
|
|
if market == 'ss':
|
|
category_data["is_certified_ss"] = True
|
|
|
|
# 그룹상품 처리
|
|
if "그룹상품" in category_text and market == 'esm':
|
|
self.logger.log(f"{market}에서 그룹상품 카테고리 발생: {category_text}", level=logging.DEBUG)
|
|
group_element = main_category_element.locator(self.category_text_locator_certified)
|
|
self.is_group_ESM = True
|
|
if await group_element.count():
|
|
category_text = await group_element.inner_text()
|
|
self.logger.log(f"{market}에서 그룹상품 텍스트: {category_text}", level=logging.DEBUG)
|
|
category_data["is_group_esm"] = True
|
|
|
|
# 카테고리 텍스트 저장
|
|
if market == 'ss':
|
|
category_data["category_text_ss"] = category_text
|
|
elif market == 'esm':
|
|
category_data["category_text_esm"] = category_text
|
|
else:
|
|
self.logger.log(f"{market}의 카테고리 텍스트 요소를 찾을 수 없습니다.", level=logging.ERROR)
|
|
|
|
return category_data
|
|
|
|
except Exception as e:
|
|
self.logger.log(f"카테고리 텍스트 가져오기 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
return {
|
|
"category_text_ss": "",
|
|
"category_text_esm": "",
|
|
"is_certified_ss": False,
|
|
"is_group_esm": False,
|
|
}
|
|
|
|
|
|
def close_db(self):
|
|
self.db_manager.close_connection() |