from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException import time, re, math import json # from utils import log from database_with_mongo import AutoPercentyProductsDB from edit.detail1 import modify_detail_page from edit.tag import edit_tag from edit.options import modify_option_page from edit.price import modify_price_page from edit.title import modify_product_title from edit.thumbnail import modify_thumb_page from edit.uploadMarket import * from edit.action_elements import click_element, return_element, wait_element import logging from edit.product_info import ProductInfo # 로거 인스턴스 가져오기 logger = logging.getLogger('default_logger') # def set_product_info(): # ''' # 상품 정보 product_info 딕셔너리 초기화 # ''' # product_info = { # 'id': None, #상품 아이디 # 'keyword_title': None, # 키워드 상품명 # 'product_title': None, # 수정 상품명 # 'trans_title': None, # 번역 상품명 # 'tao_high_price': None, # 타오바오의 최고 가격 # 'tao_low_price': None, # 타오바오의 최저 가격 # 'option_high_price': None, # 선택된 옵션 최고 가격 # 'option_low_price': None, # 선택된 옵션 최저 가격 # 'main_image_url': None, # 상품 메인썸네일 이미지 URL 리스트 # 'image_urls': [], # 상품 옵션 이미지 URL 리스트 # 'detail_image_urls': [], # 상품 상세페이지 이미지 URL 리스트 # 'thumb_image_urls': [], # 상품 썸네일 이미지 URL 리스트 # 'naver_code': None, # 네이버 카테고리 코드 # 'naver_avg_price': None, # 네이버 평균가격 # 'weight': None, # 상품 무게 # 'w_delv_fee': None, # 상품 무게배송비 # 'plus_fee': None, # 더하기마진 # 'return_fee': None, # 반품비 # 'init_delv_fee': None, # 초기반품비 # 'exchange_fee': None, # 교환배송비 # } # return product_info # 함수 이름과 해당하는 작업 함수 매핑 # function_mapping = { # 'tag_modification': edit_tag, # 'option_modification': modify_option_page, # 'detail_page_modification': modify_detail_page, # 'thumbnail_modification': modify_thumb_page, # 'price_modification': modify_price_page, # 'title_modification': modify_product_title, # } def should_execute_step(step_name, login_info): if step_name == 'tag_modification': return login_info.whether_modifyProductTag elif step_name == 'thumbnail_modification': return login_info.whether_modifyProductThumb elif step_name == 'option_modification': return login_info.whether_modifyProductOptions elif step_name == 'detail_page_modification': return login_info.whether_modifyProductDetail elif step_name == 'price_modification': return login_info.whether_modifyProductPrice else: return True def perform_step(db, step_name, action, current_user, *args): """ 단계별 작업을 수행하는 함수. """ product_info = args[1] try: action(*args) # action 함수에 product_info 및 *args를 전달합니다. db.update_process_step(product_info.id, step_name, current_user) logger.debug(f"Step {step_name} completed for product {product_info.id}.") except Exception as e: logger.error(f"Error performing step {step_name} for product {product_info.id}: {e}", exc_info=True) def load_json_naver_codes(filename): codes = [] with open(filename, "r", encoding='utf-8') as file: for line in file: try: item = json.loads(line) codes.append(item) except json.JSONDecodeError as e: logger.error(f"Error decoding JSON: {e}", exc_info=True) return codes def wait_for_javascript(driver, timeout=20): try: WebDriverWait(driver, timeout).until(lambda d: d.execute_script('return document.readyState') == 'complete') except TimeoutException: logger.debug("페이지 로딩 실패: 시간 초과") def modify_products(driver, gemini, translator, mongo_config, login_info, set_num_modify): # product_info = set_product_info() # 상품정보리스트 생성 autoPercentyProductsDB = AutoPercentyProductsDB(mongo_config) # autoPercentyProductsDB.reset_product_by_id('660d636271e50111cf71284e') # autoPercentyProductsDB.reset_product_by_id('660d635371e50111cf712842') try: if login_info['per_mode']: current_user = login_info['per_email'] else: current_user = login_info['per_em_email'] logger.debug(f"현재 작업중인 사용자 지정 : {current_user}") except Exception as e: logger.debug(f"현재 작업중인 사용자 지정 중 에러발생 : {e}", exc_info=True) # MongoDB에 연결 try: client = mongo_config.client # self.login_db = self.mongoConfig.get_db() # mongo_config 인스턴스를 통해 데이터베이스 객체를 가져옴 db = client['taobao_project'] # products_collection = db['AutoPercenty_Produtcs'] delv_collection = db['delv_fee'] except Exception as e: logger.debug(f"DB연결 중 에러발생 : {e}", exc_info=True) set_num_modify = int(set_num_modify) logger.debug(f"수정대상 상품 수 설정 : {set_num_modify}") # 한 번만 호출하여 메모리에 로드 json_naver_codes = load_json_naver_codes("Percenty_SS_code.json") # 총 상품 수 확인을 위해 모든 자바스크립트가 로드 될때까지 기다리기 logger.debug("모든 JS 로드 대기") wait_for_javascript(driver) logger.debug("총 상품 수 확인을 위한 JS 로드 완료") # 총 상품 수 확인 try: total_products_element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.XPATH, "//span[contains(.,'총')]")) ) total_products_text = total_products_element.text total_products = int(''.join(filter(str.isdigit, total_products_text))) logger.debug(f"총 상품수 : {total_products}") total_pages = (total_products + 19) // 20 except Exception as e: logger.debug(f"총 상품 수 확인 중 오류 발생: 요소를 찾을 수 없습니다.{e}", exc_info=True) if set_num_modify == 0: pass else: total_products = set_num_modify logger.debug(f"수정 대상 상품수 재설정 : {total_products}") current_page = 1 completed_products = 0 product_infos = [] while current_page <= total_pages: # 현재 페이지의 상품 수 계산 products_on_page = min(20, total_products - completed_products) for i in range(1, products_on_page + 1): # try: logger.debug(f"{current_page}페이지-{i}번 상품 수정시작") product_infos.append(ProductInfo()) # 1번 상품과 나머지 상품들에 대한 XPATH 처리 # 상품 수정 관련 작업을 조건에 따라 수행하는 로직 step_conditions = { 'tag_modification': login_info["whether_modifyProductTag"], 'thumbnail_modification': login_info["whether_modifyProductThumb"], 'option_modification': login_info["whether_modifyProductOptions"], 'detail_page_modification': login_info["whether_modifyProductDetail"], 'price_modification': login_info["whether_modifyProductPrice"], 'title_modification': login_info["whether_modifyProductName"], # 'trans_detailImage': login_info["whether_modifyImageTanslation"], 'upload_to_market': login_info["whether_uploadToMarket"], 'simpleMode': login_info["whether_simpleMode"], } steps_and_actions = [ ('tag_modification', edit_tag, (driver, product_infos[i-1])), ('thumbnail_modification', modify_thumb_page, (driver, product_infos[i-1])), ('option_modification', modify_option_page, (driver, product_infos[i-1], gemini, translator, login_info)), ('detail_page_modification', modify_detail_page, (driver, product_infos[i-1], gemini, translator, delv_collection, json_naver_codes, login_info)), ('price_modification', modify_price_page, (driver, product_infos[i-1])), ('title_modification', modify_product_title, (driver, product_infos[i-1], login_info)), # ('trans_detailImage', tran_detail_image, (driver, product_infos[i-1])), ('upload_to_market', upload_to_market, (driver, product_infos[i-1])), ] # 상품ID 복사 try: product_id_element = return_element(driver,"XPATH",f"//div[{i}]/li/div/div/div[2]/div/div/div/div[3]/div[3]/span[2]/span",10) product_id = product_id_element.get_attribute('innerText') # product_id = f"ID:{product_id}" product_infos[i-1].id = product_id logger.debug(f"상품ID : {product_id}") except Exception as e: logger.debug(f"상품ID 복사 중 오류 발생: 요소를 찾을 수 없습니다. : {e}", exc_info=True) # 상품ID 처리 여부 판단 incomplete_steps = autoPercentyProductsDB.is_step_incomplete(product_id) if incomplete_steps is True: pass # 아래는 미완성 목록이 있을 경우 해당부분만 실행하는 코드였으나, 순서대로 코드가 실행되지 않을 경우 # 다음 단계의 코드 실행중 에러발생여지가 많아서, 완료코드가 들어간 ID가 아닌 이상 모든 ID에 대해 # 모든 단계를 실행하게 하기위해 아래 부분은 주석처리 # elif isinstance(incomplete_steps, list) and len(incomplete_steps) < 6: # # incomplete_steps가 리스트 타입이고, 길이가 6 미만인 경우에만 미완성된 단계들에 대해 처리합니다. # logger.debug(f"총 [{len(incomplete_steps)}]개의 미완성 목록을 발견") # logger.debug(f"미완성 스텝 목록 : {incomplete_steps}") # product_infos[i-1] = autoPercentyProductsDB.get_product_info_by_id(product_id) # logger.debug("기존 product_info를 가져옵니다.") # # 완료되지 않은 단계에 해당하는 작업을 수행합니다. # click_to_product_title_for_modify_xpath = f"//div[{i}]/li/div/div/div[2]/div/div/div/div" # click_element(driver, 'XPATH', click_to_product_title_for_modify_xpath, 10, 'js') # logger.debug("상품 수정 페이지 클릭") # time.sleep(2) # for step_name, action, args in steps_and_actions: # if step_name in incomplete_steps: # # 각 단계별 실행 여부 판단 # if step_conditions.get(step_name, False): # perform_step(autoPercentyProductsDB, step_name, action, current_user, *args) # autoPercentyProductsDB.finalize_product_processing(product_id, current_user, product_infos[i-1]) # logger.info(f"상품 ID {product_id}의 미완성 목록을 완성했습니다.") # pass else: # 여기에는 incomplete_steps가 False이거나 리스트의 길이가 6인 경우. # 이는 상품이 수정된 적 없거나 모든 단계가 미완성인 상태를 의미. autoPercentyProductsDB.initialize_process_steps(product_id, current_user) logger.info(f"상품 ID {product_id}를 초기화 하고 상품수정을 시작합니다.") try: p_main_title_xpath = f"//div[{i}]/li/div/div/div[2]/div/div/div[1]/div[1]/span[2]" # p_main_title_xpath = f"//div[{i}]/li/div/div/div[2]/div/div/div/div/span" p_main_title_element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.XPATH, p_main_title_xpath)) ) p_main_title_text = p_main_title_element.text if not p_main_title_text: logger.debug("상품명 다시 가져오기") p_main_title_text = p_main_title_element.get_attribute('innerHTML').strip() product_infos[i-1].keyword_title = p_main_title_text logger.debug(f"상품명 확인 : {p_main_title_text}") if p_main_title_text == "수집 오류 발생": logger.debug("수집 오류 발생한 상품은 건너뜁니다.") continue # 다음 상품으로 넘어감 # 여기서부터는 "상품명"이 확인되었거나, 다른 조건에 해당하는 경우 상품 수정 로직을 계속 진행합니다. except Exception as e: logger.debug(f"상품명 확인 중 오류 발생: {e}", exc_info=True) continue # 해당 상품을 건너뛰고 다음 상품으로 넘어감 # if i == 1: # xpath_pattern = f"//div[3]/div/span[2]" # # 1번상품 xpath=//div[3]/div/span[2] # # 2번상품 xpath=//div[2]/li/div/div/div[2]/div/div/div/div[3]/div/span[2] # # 3번상품 xpath=//div[3]/li/div/div/div[2]/div/div/div/div[3]/div/span[2] # # 20번상품 xpath=//div[20]/li/div/div/div[2]/div/div/div/div[3]/div/span[2] # else: # # xpath_pattern = f"//div[2]/li[{i}]/div/div/div[2]/div/div/div/div[3]/div/span[2]" # xpath_pattern = f"//div[{i}]/li/div/div/div[2]/div/div/div/div[3]/div/span[2]" try: xpath_pattern = f"//div[{i}]/li/div/div/div[2]/div/div/div[1]/div[3]/div[1]/span[2]" product_tao_price_element = driver.find_element(By.XPATH, xpath_pattern) product_tao_price = product_tao_price_element.text logger.debug(f"product_tao_price : {product_tao_price}") # pattern = r'¥(\d+)~(\d+)' # pattern = r'¥(\d{1,3}(?:,\d{3})*)~(\d{1,3}(?:,\d{3})*)' # pattern = r'¥(\d{1,3}(?:,\d{3})*|\d+\.\d+)~(\d{1,3}(?:,\d{3})*|\d+\.\d+)' # 범위 또는 단일 값을 추출하기 위한 패턴 수정 pattern = r'¥(\d{1,3}(?:,\d{3})*|\d+\.\d+)(?:~(\d{1,3}(?:,\d{3})*|\d+\.\d+))?' match = re.search(pattern, product_tao_price) if match: if match.group(2): # 범위의 두 번째 값이 존재하는 경우 # 쉼표를 제거하고, 첫 번째 값 올림 처리 product_low_cost = math.ceil(float(match.group(1).replace(',', ''))) # 쉼표를 제거하고, 두 번째 값 올림 처리 product_high_cost = math.ceil(float(match.group(2).replace(',', ''))) else: # 단일 값만 존재하는 경우 product_low_cost = math.ceil(float(match.group(1).replace(',', ''))) product_high_cost = product_low_cost # 저가와 고가 모두 같은 값 할당 logger.debug(f"낮은 원가 : {product_low_cost}") logger.debug(f"높은 원가 : {product_high_cost}") else: logger.debug("상품 원가 수집 오류 발생: 요소를 찾을 수 없습니다.") logger.debug("원가를 기본값으로 할당합니다.") product_low_cost = 100 product_high_cost = 100 product_infos[i-1].tao_low_price = product_low_cost product_infos[i-1].tao_high_price = product_high_cost except Exception as e: logger.debug(f"상품 상세 정보 수집 중 오류 발생: {e}", exc_info=True) # 상품 이미지 복사 try: product_image_element = return_element(driver,"XPATH",f"//div[{i}]/li/div/div/div[1]/div/div[2]/div/div/img",10) except Exception as e: logger.debug(f"상품 이미지 URL 복사 중 오류 발생: 요소를 찾을 수 없습니다. : {e}", exc_info=True) product_image_url = product_image_element.get_attribute('src') product_infos[i-1].main_image_url = product_image_url logger.debug(f"product_image_url : {product_image_url}") # 상품 수정 시작 # try: # product_title_element = WebDriverWait(driver, 10).until( # EC.presence_of_element_located((By.XPATH, (f"//div[{i}]/li/div/div/div[2]/div/div/div/div"))) # # EC.presence_of_element_located((By.CSS_SELECTOR, ".signList > .ant-btn-default > span")) # ) # except Exception as e: # logger.debug(f"상품 수정시작 중 오류 발생: 요소를 찾을 수 없습니다.{e}", exc_info=True) # # product_title_element = driver.find_element(By.XPATH, (f"//div[{i}]/li/div/div/div[2]/div/div/div/div")) # driver.execute_script("arguments[0].click();", product_title_element)# 상품 수정 페이지 자바스크립트로 열기 try: click_to_product_title_for_modify_xpath = f"//div[{i}]/li/div/div/div[2]/div/div/div/div" click_element(driver, 'XPATH', click_to_product_title_for_modify_xpath, 10, 'js') logger.debug("상품 수정 페이지 클릭") time.sleep(0.3) tao_title_text_xpath = "//div[@id='productMainContentContainerId']/div/div/div[6]/div/div/span" tao_title = return_element(driver, 'XPATH', tao_title_text_xpath, 10) logger.debug(f"tao_title 텍스트 : {tao_title.text}") product_infos[i-1].tao_title = tao_title.text # 상품수정 페이지에 표시된 퍼센티카테고리 텍스트 수집 per_cat = return_element(driver, 'XPATH', '//div[8]/div/div/div[2]/div/div/div/span[2]/div/div', 10) logger.debug(f"퍼센티 등록 화면에 표시된 카테고리 텍스트 : {per_cat.text}") product_infos[i-1].per_cat_code = per_cat.text except Exception as e: logger.debug(f"상품 수정 페이지 클릭과 Tao_Title_Text 요소 찾는 중 오류 발생: {e}", exc_info=True) # # 이미지 주소 복사 # # image_element = driver.find_element(By.XPATH, "//div[@id='productMainContentContainerId']/div/div/div/div/div[2]/div/img") # try: # image_element = WebDriverWait(driver, 10).until( # # EC.presence_of_element_located((By.CSS_SELECTOR, ".sc-gFnajm")) # EC.presence_of_element_located((By.CSS_SELECTOR, ".sc-iaJaUu")) # ) # except Exception as e: # logger.debug(f"상품 이미지 주소 중 오류 발생: 요소를 찾을 수 없습니다. : {e}", exc_info=True) # # image_element = driver.find_element(By.CSS_SELECTOR, ".sc-gFnajm") # image_src = image_element.get_attribute("src") # logger.debug(f"상품 이미지 주소 : {image_src}") # #상품명 복사 # product_title_element = driver.find_element(By.XPATH, "//div[@id='productMainContentContainerId']/div/div/div/div/div[5]/div/span/input") # # product_title_element = driver.find_element(By.CSS_SELECTOR, ".ant-input-affix-wrapper-focused > .ant-input") # try: # product_title_element = WebDriverWait(driver, 10).until( # EC.presence_of_element_located((By.XPATH, "//div[5]/div/span/input")) # ) # except Exception as e: # logger.debug(f"상품명 복사 중 오류 발생: 요소를 찾을 수 없습니다. : {e}", exc_info=True) # # product_title_element = driver.find_element(By.XPATH, "//div[5]/div/span/input") # product_title = product_title_element.get_attribute("value") product_infos[i-1].main_image_url = product_image_url # 각 단계별로 수정 작업 수행 for step_name, action, args in steps_and_actions: # 각 단계별 실행 여부 판단 if step_conditions.get(step_name, False): perform_step(autoPercentyProductsDB, step_name, action, current_user, *args) # 모든 단계가 완료되었다면, 상품 처리 최종화 autoPercentyProductsDB.finalize_product_processing(product_infos[i-1].id, current_user, product_infos[i-1]) # try: # # 각 단계별 작업 수행 # perform_step(autoPercentyProductsDB, 'tag_modification', product_infos[i-1], current_user, driver) # perform_step(autoPercentyProductsDB, 'option_modification', product_infos[i-1], current_user, driver) # perform_step(autoPercentyProductsDB, 'detail_page_modification', product_infos[i-1], current_user, driver, gemini, delv_collection, json_naver_codes) # perform_step(autoPercentyProductsDB, 'thumbnail_modification', product_infos[i-1], current_user, driver) # perform_step(autoPercentyProductsDB, 'price_modification', product_infos[i-1], current_user, driver) # logger.debug("모든 단계의 수정 작업이 완료되었습니다.") # except Exception as e: # logger.error(f"수정 작업 중 예외 발생: {e}", exc_info=True) # logger.debug("키워드 수정작업 시작") # edit_tag(driver, product_infos[i-1]) # autoPercentyProductsDB.update_process_step(product_id, step_name="step1") # logger.debug("키워드 수정작업 완료") # logger.debug("옵션 수정작업 시작") # modify_option_page(driver, product_infos[i-1]) # logger.debug("옵션 수정작업 완료") # # 상품 수정 작업 수행 # logger.debug("상세페이지 수정작업 시작") # modify_detail_page(driver, gemini, product_infos[i-1], delv_collection, json_naver_codes) # logger.debug("상세페이지 수정작업 완료") # logger.debug("썸네일 수정작업 시작") # modify_thumb_page(driver, product_infos[i-1]) # logger.debug("썸네일 수정작업 완료") # logger.debug("가격 수정작업 시작") # modify_price_page(driver, product_infos[i-1]) # logger.debug("가격 수정작업 완료") # logger.debug("상품명 수정작업 시작") # # modify_product_title(driver, product_infos[i-1]) # logger.debug("......상품명 메서드 작성 중......") # logger.debug("상품명 수정작업 완료") # 상품수정페이지 저장 버튼 클릭 logger.debug("상품 수정 저장 중......") click_element(driver, 'CSS_SELECTOR', '.ant-col:nth-child(9) > .ant-btn', wait_time=10, click_type='js') # 상품수정페이지 닫기 버튼 클릭 time.sleep(1) ActionChains(driver).send_keys(Keys.ESCAPE).perform() logger.debug(f"상품 {product_id} 처리 완료.") completed_products += 1 logger.debug(f"진행 상황: {completed_products}/{total_products} ({(completed_products/total_products)*100:.2f}%)") # except Exception as e: # logger.debug(f"상품 {i} 확인 중 오류 발생: {e}", exc_info=True) # 다음 페이지로 이동 if current_page < total_pages: try: next_page_button = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, ".ant-pagination-next > .ant-pagination-item-link")) ) # next_page_button = driver.find_element(By.CSS_SELECTOR, ".ant-pagination-next > .ant-pagination-item-link") # next_page_button.click() driver.execute_script("arguments[0].click();", next_page_button) time.sleep(2) current_page += 1 except Exception as e: logger.debug(f"다음 페이지로 이동하는 중 오류 발생: {e}", exc_info=True) break else: logger.debug("모든 페이지 처리 완료. 작업 종료.") break logger.debug("상품 수정 작업이 완료되었습니다.")