추가수정
This commit is contained in:
parent
25b6e7ba63
commit
8f96b7198d
|
|
@ -1,5 +1,4 @@
|
|||
from playwright.sync_api import sync_playwright
|
||||
<<<<<<< HEAD
|
||||
import random
|
||||
import logging
|
||||
|
||||
|
|
@ -11,23 +10,25 @@ def trans_text(original_text):
|
|||
try:
|
||||
# Playwright 브라우저 인스턴스 생성 (헤드리스 모드)
|
||||
browser = p.chromium.launch(headless=True) # 여기서 headless=False로 설정하면 GUI 모드로 실행
|
||||
page = browser.new_page()
|
||||
|
||||
# PC 사용자 에이전트 중 하나를 랜덤하게 선택
|
||||
USER_AGENTS = [
|
||||
# 동영상 녹화 설정을 포함하여 브라우저 컨텍스트 생성
|
||||
context = browser.new_context(
|
||||
user_agent=random.choice([
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.0.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 12_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 OPR/85.0.0.0"
|
||||
]
|
||||
random_user_agent = random.choice(USER_AGENTS)
|
||||
|
||||
context = browser.new_context(user_agent=random_user_agent)
|
||||
]),
|
||||
# # 동영상 녹화 설정 추가
|
||||
# record_video_dir="videos/", # 동영상이 저장될 디렉토리 지정
|
||||
# record_video_size={"width": 1280, "height": 720} # 동영상 크기 지정
|
||||
)
|
||||
page = context.new_page()
|
||||
|
||||
# DeepL 웹사이트 접속
|
||||
page.goto('https://www.deepl.com/ko/translator')
|
||||
# page.goto('https://www.deepl.com/ko/translator')
|
||||
page.goto('https://www.deepl.com/translator#zh/ko/'+ original_text)
|
||||
|
||||
# JavaScript를 사용하여 편집 가능한 div에 텍스트 입력
|
||||
page.evaluate("""(text) => {
|
||||
|
|
@ -42,7 +43,9 @@ def trans_text(original_text):
|
|||
# "visible": 요소가 DOM에 삽입되어 있으며 렌더링된 상태(화면에 보이는 상태)임을 의미합니다. CSS를 통해 숨겨진 요소는 visible 상태에 해당하지 않습니다.
|
||||
# "hidden": 요소가 DOM에 존재하지 않거나 렌더링되지 않아 화면에 보이지 않음을 의미합니다. 이는 요소가 아예 존재하지 않거나, CSS 등을 통해 숨겨진 경우에 해당합니다.
|
||||
# timeout=10000 (10초 내에 해당요소가 "state" 될때까지 대기)
|
||||
page.wait_for_function('document.querySelector("[data-testid="translator-target-input"]").textContent.length > 0')
|
||||
# page.wait_for_function('document.querySelector("[data-testid="translator-target-input"]").textContent.length > 0')
|
||||
# page.wait_for_function('document.querySelector(\'[data-testid="translator-target-input"]\').textContent.length > 0')
|
||||
page.wait_for_function("document.querySelector(\"[data-testid='translator-target-input']\").textContent.length > 0")
|
||||
|
||||
page.wait_for_selector('[data-testid="translator-target-input"]', state="visible", timeout=10000)
|
||||
translated_text = page.text_content('[data-testid="translator-target-input"]')
|
||||
|
|
@ -68,10 +71,75 @@ def trans_text(original_text):
|
|||
return "에러발생으로 인한 번역 실패."
|
||||
|
||||
finally:
|
||||
if browser: # browser가 초기화되었는지 확인
|
||||
if context: # context가 초기화되었는지 확인하고, 종료
|
||||
context.close()
|
||||
if browser: # browser가 초기화되었는지 확인하고, 종료
|
||||
browser.close()
|
||||
logger.info("playwright Browser 닫힘.")
|
||||
|
||||
return translated_text
|
||||
=======
|
||||
>>>>>>> 4b4ff58484a1cc6a2080061367ea02ec8d38e8b7
|
||||
|
||||
def trans_list_text(original_texts):
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=True) # 여기서 headless=False로 설정하면 GUI 모드로 실행
|
||||
# 동영상 녹화 설정을 포함하여 브라우저 컨텍스트 생성
|
||||
context = browser.new_context(
|
||||
user_agent=random.choice([
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.0.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 12_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 OPR/85.0.0.0"
|
||||
]),
|
||||
# # 동영상 녹화 설정 추가
|
||||
# record_video_dir="videos/", # 동영상이 저장될 디렉토리 지정
|
||||
# record_video_size={"width": 1280, "height": 720} # 동영상 크기 지정
|
||||
)
|
||||
page = context.new_page()
|
||||
page.goto('https://www.deepl.com/translator#zh/ko/')
|
||||
|
||||
translated_texts = [] # 번역된 텍스트를 저장할 리스트
|
||||
|
||||
for text in original_texts:
|
||||
# contenteditable 속성을 가진 div에 텍스트를 직접 설정
|
||||
# page.evaluate("""(text) => {
|
||||
# const editor = document.querySelector('[data-testid="translator-source-input"] div[contenteditable="true"]');
|
||||
# editor.textContent = text;
|
||||
# }""", text)
|
||||
|
||||
page.evaluate("""(text) => {
|
||||
const editor = document.querySelector('[data-testid="translator-source-input"] div[contenteditable="true"]');
|
||||
editor.textContent = text;
|
||||
const inputEvent = new Event('input', { bubbles: true });
|
||||
editor.dispatchEvent(inputEvent);
|
||||
}""", text)
|
||||
# 번역이 완료될 때까지 기다림
|
||||
|
||||
page.wait_for_function("document.querySelector(\"[data-testid='translator-target-input']\").textContent.length > 0")
|
||||
|
||||
page.wait_for_selector('[data-testid="translator-target-input"]', state="visible", timeout=10000)
|
||||
|
||||
# 번역된 텍스트 추출
|
||||
translated_text = page.text_content('[data-testid="translator-target-input"]')
|
||||
translated_texts.append(translated_text.strip())
|
||||
|
||||
browser.close()
|
||||
return translated_texts
|
||||
|
||||
# ttt = '''
|
||||
# 贴片款(小号) 240*300mm\n\n
|
||||
# 贴片款(中号) 440*315mm\n\n
|
||||
# 贴片款(大号) 590*440mm\n\n
|
||||
# '''
|
||||
# tttt = trans_text(ttt)
|
||||
|
||||
|
||||
# # test = []
|
||||
# # test.append('贴片款(小号) 240*300mm')
|
||||
# # test.append('贴片款(中号) 440*315mm')
|
||||
# # test.append('贴片款(大号) 590*440mm')
|
||||
|
||||
# # translated_text = trans_list_text(test)
|
||||
|
||||
|
||||
# print(f"번역된 글자 : {ttt}")
|
||||
|
|
@ -240,6 +240,17 @@ def find_delivery_fee(weight, delv_collection):
|
|||
|
||||
# return product_info_text
|
||||
|
||||
def original_html(current_html):
|
||||
# BeautifulSoup 객체 생성
|
||||
soup = BeautifulSoup(current_html, 'html.parser')
|
||||
|
||||
# "img class='image_resized'" 태그만 추출
|
||||
resized_images = soup.find_all('img', class_='image_resized')
|
||||
|
||||
# 추출된 태그들로 새로운 HTML 문자열 생성
|
||||
original_html_text = ''.join(str(tag) for tag in resized_images)
|
||||
|
||||
return original_html_text
|
||||
|
||||
def NS_info_with_HTML(products):
|
||||
# 상품 정보를 담을 5x1 표 생성
|
||||
|
|
@ -534,16 +545,17 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
try:
|
||||
# # data-value 속성 값을 가져옵니다.
|
||||
current_value = textarea.get_attribute("data-value")
|
||||
original_html_tags = original_html(current_value)
|
||||
# logger.debug(f"현재 속성값 :{current_value}")
|
||||
product_info.current_value = current_value
|
||||
product_info.current_value = original_html_tags
|
||||
|
||||
logger.debug("현재 속성값 수집 완료")
|
||||
|
||||
# # ============번역을 위한 사전작업============
|
||||
# 이미지 Url 수집
|
||||
# translated_image_urls = [] # 번역된 이미지 URL들을 저장할 리스트
|
||||
detail_images = fetch_image_urls(current_value)
|
||||
logger.debug(f"detail_images URLs \n {detail_images}")
|
||||
detail_images = fetch_image_urls(original_html_tags)
|
||||
# logger.debug(f"detail_images URLs \n {detail_images}")
|
||||
product_info.detail_image_urls = detail_images
|
||||
|
||||
# 원본 URL을 빈문자열로 대체
|
||||
|
|
@ -554,13 +566,12 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
# # ============번역을 위한 사전작업============
|
||||
|
||||
logger.debug("product_info_card 결합.")
|
||||
new_value = product_info_card + cost_add_text + current_value
|
||||
new_value = product_info_card +'<br><br>' + cost_add_text +'<br><br>' +original_html_tags
|
||||
# textarea.send_keys(product_info_card)
|
||||
time.sleep(0.2)
|
||||
|
||||
logger.debug("결합.")
|
||||
# 결합된 값을 다시 textarea에 설정
|
||||
try:
|
||||
# # data-value 속성을 새로운 값으로 설정합니다.
|
||||
driver.execute_script("arguments[0].setAttribute('data-value', arguments[1]);", textarea, new_value)
|
||||
|
||||
|
|
@ -572,8 +583,7 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
# driver.execute_script(script, textarea, new_value)
|
||||
time.sleep(0.5)
|
||||
logger.debug("새로운 값이 성공적으로 설정됨.")
|
||||
except Exception as e:
|
||||
logger.error(f"새로운 값 설정 중 오류 발생: {e}", exc_info=True)
|
||||
|
||||
time.sleep(0.2)
|
||||
|
||||
# textarea.send_keys(Keys.ENTER)
|
||||
|
|
@ -626,8 +636,10 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
|
||||
|
||||
|
||||
trans_img_tag = login_info["whether_modifyImageTanslation"]
|
||||
logger.debug(f"whether_modifyImageTanslation : {trans_img_tag}")
|
||||
|
||||
if login_info.whether_modifyImageTanslation:
|
||||
if login_info["whether_modifyImageTanslation"]:
|
||||
logger.debug("상세페이지 이미지 번역 시작")
|
||||
try:
|
||||
for i, detail_image in enumerate(detail_images):
|
||||
|
|
@ -650,9 +662,6 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
logger.debug("====번역이미지 붙여넣기 완료=====")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
logger.debug("상세페이지 편집 저장")
|
||||
|
||||
click_element(driver, "XPATH", save_button_xpath, 5, 'js')
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import sys, re
|
|||
import numpy as np
|
||||
from naver_search import parse_naver_shopping
|
||||
from edit.naver_code import find_naver_code
|
||||
from edit.action_elements import click_element, return_element
|
||||
from edit.action_elements import click_element, return_element, click_and_confirm_tab
|
||||
from img_trans.image_trans import image_trans
|
||||
import logging
|
||||
from bs4 import BeautifulSoup
|
||||
|
|
@ -22,6 +22,7 @@ logger = logging.getLogger('default_logger')
|
|||
|
||||
avg_price = 0
|
||||
|
||||
|
||||
def paste_image_in_editor(driver, editor_element):
|
||||
action = ActionChains(driver)
|
||||
action.click(editor_element) # 에디터를 클릭하여 포커스를 맞춥니다.
|
||||
|
|
@ -72,27 +73,25 @@ def fetch_image_urls(html_content):
|
|||
return image_urls
|
||||
|
||||
|
||||
def safe_generate_content(gemini, image_src, product_title, max_retries=3, initial_wait=1):
|
||||
def safe_generate_content(gemini, product_info, max_retries=3, initial_wait=1):
|
||||
retry_count = 0
|
||||
wait_time = initial_wait
|
||||
|
||||
while retry_count < max_retries:
|
||||
try:
|
||||
return gemini.generate_description(image_src, product_title)
|
||||
return gemini.generate_description(product_info)
|
||||
# except InternalServerError as e:
|
||||
# logger.debug(f"Retry {retry_count + 1}/{max_retries} for InternalServerError")
|
||||
# time.sleep(wait_time)
|
||||
# wait_time *= 2 # Exponential backoff
|
||||
# retry_count += 1
|
||||
except Exception as e:
|
||||
logger.debug(f"예외 발생 : {e}", exc_info=True)
|
||||
logger.debug(f"예외 발생으로 재시도 : {retry_count + 1}/{max_retries}")
|
||||
time.sleep(wait_time)
|
||||
wait_time *= 2 # Exponential backoff
|
||||
retry_count += 1
|
||||
|
||||
# Final attempt without catching the exception
|
||||
return gemini.generate_content(image_src, product_title)
|
||||
|
||||
def selling_price(tao_bao_price, shipping_fee=6500, target_margin_rate=0.29, market_fee_rate=0.13,
|
||||
card_fee_rate=0.035, exchange_rate=200):
|
||||
"""
|
||||
|
|
@ -186,65 +185,76 @@ def find_delivery_fee(weight, delv_collection):
|
|||
return fees[closest_weight_index]
|
||||
|
||||
#네이버쇼핑 함수
|
||||
def NS_info(products):
|
||||
# 각 상품 정보에 접근하여 텍스트로 가공
|
||||
product_info_text = ""
|
||||
global avg_price
|
||||
avg_price = 0
|
||||
prices = []
|
||||
# def NS_info(products):
|
||||
# # 각 상품 정보에 접근하여 텍스트로 가공
|
||||
# product_info_text = ""
|
||||
# global avg_price
|
||||
# avg_price = 0
|
||||
# prices = []
|
||||
|
||||
for index, product in enumerate(products):
|
||||
idx = index + 1
|
||||
product_info_text += '<div class="card">\n' # 카드 시작 태그
|
||||
product_info_text += f"=================={idx} 번째 네이버 상품========================"
|
||||
# 이미지 태그
|
||||
product_info_text += f'<img src="{product["imageUrl"]}" width="200" alt="상품 이미지">\n'
|
||||
# for index, product in enumerate(products):
|
||||
# idx = index + 1
|
||||
# product_info_text += '<div class="card">\n' # 카드 시작 태그
|
||||
# product_info_text += f"=================={idx} 번째 네이버 상품========================"
|
||||
# # 이미지 태그
|
||||
# product_info_text += f'<img src="{product["imageUrl"]}" width="200" alt="상품 이미지">\n'
|
||||
|
||||
# 상품 세부 정보 태그 시작
|
||||
product_info_text += '<div class="product-details">\n'
|
||||
# # 상품 세부 정보 태그 시작
|
||||
# product_info_text += '<div class="product-details">\n'
|
||||
|
||||
# 상품명 태그
|
||||
# product_info_text += f'<div class="product-name"><strong>상품명 : {product["productTitle"]}</strong></div>\n'
|
||||
product_info_text += f'<div class="product-name"><h3>상품명 : {product["productTitle"]}</h3></div>\n'
|
||||
# 가격 태그
|
||||
# 상품 가격을 숫자로 변환하고 포맷
|
||||
if isinstance(product["price"], str):
|
||||
# 문자열인 경우 숫자로 변환
|
||||
price = int(product["price"].replace(",", "")) # 쉼표 제거 후 정수 변환
|
||||
else:
|
||||
price = product["price"] # 이미 숫자인 경우 변환 없음
|
||||
# # 상품명 태그
|
||||
# # product_info_text += f'<div class="product-name"><strong>상품명 : {product["productTitle"]}</strong></div>\n'
|
||||
# product_info_text += f'<div class="product-name"><h3>상품명 : {product["productTitle"]}</h3></div>\n'
|
||||
# # 가격 태그
|
||||
# # 상품 가격을 숫자로 변환하고 포맷
|
||||
# if isinstance(product["price"], str):
|
||||
# # 문자열인 경우 숫자로 변환
|
||||
# price = int(product["price"].replace(",", "")) # 쉼표 제거 후 정수 변환
|
||||
# else:
|
||||
# price = product["price"] # 이미 숫자인 경우 변환 없음
|
||||
|
||||
prices.append(price)
|
||||
formatted_price = format(price, ",") + "원" # 천 단위 구분으로 포맷
|
||||
product_info_text += f'<div class="price">가격: {formatted_price}</div>'
|
||||
# prices.append(price)
|
||||
# formatted_price = format(price, ",") + "원" # 천 단위 구분으로 포맷
|
||||
# product_info_text += f'<div class="price">가격: {formatted_price}</div>'
|
||||
|
||||
# 순위 태그
|
||||
product_info_text += f'<div class="price">순위: {product["rank"]}</div>\n'
|
||||
# # 순위 태그
|
||||
# product_info_text += f'<div class="price">순위: {product["rank"]}</div>\n'
|
||||
|
||||
product_info_text += "=================================================================="
|
||||
# product_info_text += "=================================================================="
|
||||
|
||||
# 상품 세부 정보 태그 종료
|
||||
product_info_text += '</div>\n'
|
||||
# # 상품 세부 정보 태그 종료
|
||||
# product_info_text += '</div>\n'
|
||||
|
||||
product_info_text += '</div>\n' # 카드 종료 태그
|
||||
avg_price += int(product["price"])
|
||||
# product_info_text += '</div>\n' # 카드 종료 태그
|
||||
# avg_price += int(product["price"])
|
||||
|
||||
# # avg_price = round(avg_price/len(products),0)
|
||||
|
||||
# if products: # products가 비어 있지 않은 경우에만 평균 가격 계산
|
||||
# avg_price = round(avg_price / len(products), 0)
|
||||
# low_price = min(prices)
|
||||
# high_price = max(prices)
|
||||
# product_info_text += f'네이버 상품의 최저 가격은 [{low_price}]'
|
||||
# product_info_text += f'네이버 상품의 평균 가격은 [{avg_price}]'
|
||||
# product_info_text += f'네이버 상품의 최고 가격은 [{high_price}]'
|
||||
|
||||
if products: # products가 비어 있지 않은 경우에만 평균 가격 계산
|
||||
avg_price = round(avg_price / len(products), 0)
|
||||
low_price = min(prices)
|
||||
high_price = max(prices)
|
||||
product_info_text += f'네이버 상품의 최저 가격은 [{low_price}]'
|
||||
product_info_text += f'네이버 상품의 평균 가격은 [{avg_price}]'
|
||||
product_info_text += f'네이버 상품의 최고 가격은 [{high_price}]'
|
||||
# return product_info_text
|
||||
|
||||
return product_info_text
|
||||
def original_html(current_html):
|
||||
# BeautifulSoup 객체 생성
|
||||
soup = BeautifulSoup(current_html, 'html.parser')
|
||||
|
||||
# "img class='image_resized'" 태그만 추출
|
||||
resized_images = soup.find_all('img', class_='image_resized')
|
||||
|
||||
# 추출된 태그들로 새로운 HTML 문자열 생성
|
||||
original_html_text = ''.join(str(tag) for tag in resized_images)
|
||||
|
||||
return original_html_text
|
||||
|
||||
def NS_info_with_HTML(products):
|
||||
# 상품 정보를 담을 5x1 표 생성
|
||||
product_info_text = "<table><tr>"
|
||||
product_info_table = "<table><tr>"
|
||||
global avg_price
|
||||
avg_price = 0
|
||||
|
||||
|
|
@ -257,44 +267,18 @@ def NS_info_with_HTML(products):
|
|||
|
||||
# 각 상품 카드를 생성하고 테이블의 셀로 추가
|
||||
product_card = create_product_card(product["productTitle"], product["imageUrl"], formatted_price, product["rank"], product["purchase"], product["review"])
|
||||
product_info_text += f"<td style='width: 170px; height: 170px;'>{product_card}</td>"
|
||||
product_info_table += f"<td style='width: 170px; height: 170px;'>{product_card}</td>"
|
||||
avg_price += price
|
||||
|
||||
product_info_text += "</tr></table>"
|
||||
product_info_table += "</tr></table>"
|
||||
|
||||
if products:
|
||||
avg_price = round(avg_price / len(products), 0)
|
||||
product_info_text += f'<div><h3>네이버 상품의 평균 가격은 {avg_price:,.0f}원입니다.</h3></div>'
|
||||
product_info_table += f'<div><h3>네이버 상품의 평균 가격은 {avg_price:,.0f}원입니다.</h3></div>'
|
||||
|
||||
return product_info_text
|
||||
return product_info_table
|
||||
|
||||
|
||||
#네이버쇼핑 함수 HTML
|
||||
def NS_info_with_HTML_ori(products):
|
||||
product_info_text = ""
|
||||
global avg_price
|
||||
avg_price = 0
|
||||
|
||||
for product in products:
|
||||
if isinstance(product["price"], str):
|
||||
price = int(product["price"].replace(",", ""))
|
||||
else:
|
||||
price = product["price"]
|
||||
formatted_price = format(price, ",") + "원"
|
||||
|
||||
# 각 상품 카드를 생성하여 테이블의 셀로 추가
|
||||
product_card = create_product_card(product["productTitle"], product["imageUrl"], formatted_price, product["rank"])
|
||||
product_info_text += f"<td>{product_card}</td>"
|
||||
avg_price += price
|
||||
|
||||
product_info_text += "</tr></table>"
|
||||
|
||||
if products:
|
||||
avg_price = round(avg_price / len(products), 0)
|
||||
product_info_text += f'<div>네이버 상품의 평균 가격은 {avg_price}원입니다.</div>'
|
||||
|
||||
return product_info_text
|
||||
|
||||
def naver_prices(products):
|
||||
prices = []
|
||||
for product in products:
|
||||
|
|
@ -337,64 +321,51 @@ def create_product_card(product_name, image_url, price, rank, purchase, review):
|
|||
return product_card
|
||||
|
||||
|
||||
def create_product_card_ori(product_name, image_url, price, rank):
|
||||
# 이미지 크기를 직접 지정
|
||||
html_text = f"""
|
||||
<table style="width: 400px; border-collapse: collapse;">
|
||||
<tr>
|
||||
<td rowspan="3" style="height: 400px; vertical-align: top;"><img src="{image_url}" style="width: 100%; max-width: 400px; height: auto;" alt="상품 이미지"></td>
|
||||
<td style="border: 1px solid #dddddd; text-align: left; padding: 8px;">가격: {price}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border: 1px solid #dddddd; text-align: left; padding: 8px;">순위: {rank}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" style="border: 1px solid #dddddd; text-align: center; vertical-align: middle; font-weight: bold;">{product_name}</td>
|
||||
</tr>
|
||||
</table>
|
||||
"""
|
||||
return html_text
|
||||
# #네이버쇼핑 함수 마크다운
|
||||
# def NS_info_markdown(products):
|
||||
# product_info_md = ""
|
||||
# global avg_price
|
||||
# avg_price = 0
|
||||
|
||||
# for index, product in enumerate(products):
|
||||
# idx = index + 1
|
||||
# # 제품명을 헤더로 표시
|
||||
# product_info_md += f"### {idx}번상품. {product['productTitle']}\n"
|
||||
|
||||
# # 이미지 추가
|
||||
# product_info_md += f"\n\n"
|
||||
|
||||
# # 가격과 순위 정보
|
||||
# if isinstance(product["price"], str):
|
||||
# price = int(product["price"].replace(",", "")) # 쉼표 제거 후 정수 변환
|
||||
# else:
|
||||
# price = product["price"]
|
||||
# formatted_price = format(price, ",") + "원"
|
||||
|
||||
# product_info_md += f"- **가격:** {formatted_price}\n"
|
||||
# product_info_md += f"- **순위:** {product['rank']}\n\n"
|
||||
|
||||
# avg_price += price # 평균 가격 계산을 위해 가격 추가
|
||||
|
||||
# if products: # products가 비어 있지 않은 경우에만 평균 가격 계산
|
||||
# avg_price = round(avg_price / len(products), 0)
|
||||
# product_info_md += f"**네이버 상품의 평균 가격은 {avg_price}원입니다.**\n"
|
||||
|
||||
# return product_info_md
|
||||
|
||||
def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver_codes, login_info):
|
||||
|
||||
detail_css = ".ant-tabs-tab:nth-child(6)"
|
||||
|
||||
thumb_data_note = "5"
|
||||
click_and_confirm_tab(driver, thumb_data_note, 10)
|
||||
|
||||
|
||||
#네이버쇼핑 함수 마크다운
|
||||
def NS_info_markdown(products):
|
||||
product_info_md = ""
|
||||
global avg_price
|
||||
avg_price = 0
|
||||
|
||||
for index, product in enumerate(products):
|
||||
idx = index + 1
|
||||
# 제품명을 헤더로 표시
|
||||
product_info_md += f"### {idx}번상품. {product['productTitle']}\n"
|
||||
|
||||
# 이미지 추가
|
||||
product_info_md += f"\n\n"
|
||||
|
||||
# 가격과 순위 정보
|
||||
if isinstance(product["price"], str):
|
||||
price = int(product["price"].replace(",", "")) # 쉼표 제거 후 정수 변환
|
||||
else:
|
||||
price = product["price"]
|
||||
formatted_price = format(price, ",") + "원"
|
||||
|
||||
product_info_md += f"- **가격:** {formatted_price}\n"
|
||||
product_info_md += f"- **순위:** {product['rank']}\n\n"
|
||||
|
||||
avg_price += price # 평균 가격 계산을 위해 가격 추가
|
||||
|
||||
if products: # products가 비어 있지 않은 경우에만 평균 가격 계산
|
||||
avg_price = round(avg_price / len(products), 0)
|
||||
product_info_md += f"**네이버 상품의 평균 가격은 {avg_price}원입니다.**\n"
|
||||
|
||||
return product_info_md
|
||||
|
||||
def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver_codes):
|
||||
product_title = product_info.init_title
|
||||
product_low_cost = product_info.tao_low_price
|
||||
product_high_cost = product_info.tao_high_price
|
||||
product_keyword = product_info.keyword_title
|
||||
# product_low_cost = product_info.tao_low_price
|
||||
# product_high_cost = product_info.tao_high_price
|
||||
product_low_cost = product_info.option_low_price
|
||||
product_high_cost = product_info.option_high_price
|
||||
image_src = product_info.main_image_url
|
||||
|
||||
try:
|
||||
|
|
@ -409,6 +380,30 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
logger.debug(f"카테고리 코드 검색 중 예외 발생 : {e}", exc_info=True)
|
||||
naver_code = ""
|
||||
|
||||
|
||||
|
||||
try:
|
||||
logger.debug("네이버쇼핑 파싱 중")
|
||||
products = parse_naver_shopping(product_keyword, naver_code, isOverseas=1, sortcount=5)
|
||||
logger.debug("네이버쇼핑 파싱 완료")
|
||||
# logger.debug(f"네이버 파싱된 상품 리스트 \n {products}")
|
||||
product_info.naver_products = products
|
||||
|
||||
# 네이버 상품가격 담기
|
||||
naver_price = naver_prices(products)
|
||||
product_info.naver_low_price = naver_price[0]
|
||||
product_info.naver_avg_price = naver_price[1]
|
||||
product_info.naver_high_price = naver_price[2]
|
||||
|
||||
# product_info_text = NS_info(products)
|
||||
product_info_card = NS_info_with_HTML(products)
|
||||
# logger.debug(f"수집된 정보 \n {product_info_card} \n")
|
||||
logger.debug("네이버쇼핑 파싱 완료")
|
||||
except Exception as e:
|
||||
logger.debug(f"네이버쇼핑 파싱 중 에러발생 : {e}", exc_info=True)
|
||||
|
||||
|
||||
|
||||
# click_element('XPATH', '/html/body/div[7]/div/div[3]/div/div[2]/div[1]/div/div[2]/div[8]/div/div/div[2]/div[1]/div/div/span[2]/div/div')
|
||||
|
||||
# testt = find_delivery_fee(7,delv_collection)
|
||||
|
|
@ -430,8 +425,8 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
except Exception as e:
|
||||
logger.debug(f"상세페이지 탭으로 이동 중 오류 발생: 요소를 찾을 수 없습니다. : {e}", exc_info=True)
|
||||
# detail_tab = driver.find_element(By.CSS_SELECTOR, ".ant-tabs-tab:nth-child(6)")
|
||||
detail_tab.click()
|
||||
time.sleep(2) # 페이지 로딩 대기
|
||||
# detail_tab.click()
|
||||
time.sleep(0.2) # 페이지 로딩 대기
|
||||
|
||||
# 상세페이지 내용 수정
|
||||
try:
|
||||
|
|
@ -446,11 +441,10 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
try:
|
||||
ActionChains(driver).move_to_element(detail_content).click().perform()
|
||||
time.sleep(1)
|
||||
|
||||
logger.debug("AI 이미지 처리중")
|
||||
# contents = bard_img(image_src)
|
||||
# contents = gemini.generate_description(image_src, product_title)
|
||||
aicontents = safe_generate_content(gemini, image_src, product_title)
|
||||
aicontents = safe_generate_content(gemini, product_info)
|
||||
logger.debug(f"{aicontents}")
|
||||
product_info.ai_contents = aicontents
|
||||
logger.debug("AI 이미지 처리 완료")
|
||||
|
|
@ -467,30 +461,54 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
logger.debug(f"추가포장비 추정 : {packing_fee}")
|
||||
product_info.packing_fee = packing_fee
|
||||
|
||||
logger.debug("네이버쇼핑 파싱 중")
|
||||
products = parse_naver_shopping(product_title, naver_code, isOverseas=1, sortcount=5)
|
||||
|
||||
logger.debug(f"네이버 파싱된 상품 리스트 \n {products}")
|
||||
product_info.naver_products = products
|
||||
|
||||
# 네이버 상품가격 담기
|
||||
naver_price = naver_prices(products)
|
||||
product_info.naver_low_price = naver_price[0]
|
||||
product_info.naver_avg_price = naver_price[1]
|
||||
product_info.naver_high_price = naver_price[2]
|
||||
|
||||
# product_info_text = NS_info(products)
|
||||
product_info_text = NS_info_with_HTML(products)
|
||||
# logger.debug(f"수집된 정보 \n {product_info_text} \n")
|
||||
logger.debug("네이버쇼핑 파싱 완료")
|
||||
except Exception as e:
|
||||
logger.debug(f"상세페이지 편집 중 에러발생 : {e}", exc_info=True)
|
||||
|
||||
|
||||
try:
|
||||
# 가격 정보 계산
|
||||
low_cost, low_margin = selling_price(product_low_cost, delv_fee, 0.29)
|
||||
high_cost, high_margin = selling_price(product_high_cost, delv_fee, 0.29)
|
||||
low_delv_fee = avg_price - low_cost
|
||||
high_delv_fee = avg_price - high_cost
|
||||
|
||||
# 마진율 계산
|
||||
low_margin_r = round((low_margin / low_cost) * 100, 2)
|
||||
high_margin_r = round((high_margin / high_cost) * 100, 2)
|
||||
|
||||
# 텍스트 조합
|
||||
cost_add_text = f"""
|
||||
<h1>==== 가격과 마진율 ====</h1><br>
|
||||
해당 제품의 네이버 평균가격은 <span style="font-size:16px;"><strong><u>{avg_price:,.0f}</u></strong></span>원입니다.<br>
|
||||
해당 제품의 무게배송비는 <span style="font-size:16px;"><strong><u>{delv_fee:,.0f}</u></strong></span>원입니다.<br>
|
||||
|
||||
**마진율 확보를 위한 판매가 범위:** {low_cost:,.0f}원 ~ {high_cost:,.0f}원<br>
|
||||
|
||||
<h3>**저가 중심 계산:**</h3><br>
|
||||
* 무게 배송비 대비: {low_delv_fee:,.0f}원 {('낮은' if low_delv_fee < 0 else '높은')} 편<br>
|
||||
* 마진율: {low_margin_r:,.2f}%<br>
|
||||
<h3>**고가중심 계산:**</h3><br>
|
||||
* 무게 배송비 대비: {high_delv_fee:,.0f}원 {('낮은' if high_delv_fee < 0 else '높은')} 편<br>
|
||||
* 마진율: {high_margin_r:,.2f}%<br>
|
||||
|
||||
<h3>**추가 정보:**</h3><br>
|
||||
<h2>* 해당 제품의 파손 여부 및 무게 오차를 고려하여 더하기 마진을 설정하세요.</h2><br>
|
||||
<h3>=======================================</h3><br><br>
|
||||
"""
|
||||
|
||||
# time.sleep(2)
|
||||
# detail_content.send_keys(cost_add_text)
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"가격정보 생성 중 에러 : {e}", exc_info=True)
|
||||
|
||||
|
||||
try:
|
||||
logger.debug("HTML 수정 버튼 클릭 .")
|
||||
html_btn_by_contains_xpath="//button[contains(.,'소스')]"
|
||||
html_btn_css = "ck.ck-button.ck-source-editing-button.ck-off.ck-button_with-text"
|
||||
html_btn_xpath="//div[@id='productMainContentContainerId']/div/div/div[2]/div[2]/div/div/div[2]/div/div/button[8]"
|
||||
click_element(driver, 'XPATH', html_btn_xpath, 5, 'js')
|
||||
click_element(driver, 'XPATH', html_btn_xpath, 5, 'ac')
|
||||
# html_btn = WebDriverWait(driver, 10).until(
|
||||
# # EC.presence_of_element_located((By.CSS_SELECTOR, ".ck-source-editing-button"))
|
||||
# EC.presence_of_element_located(By.CLASS_NAME, html_btn_xpath)
|
||||
|
|
@ -498,7 +516,6 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
# )
|
||||
# logger.debug("HTML 수정 버튼을 성공적으로 찾았습니다.")
|
||||
# html_btn.click()
|
||||
logger.debug("HTML 수정 버튼 클릭 .")
|
||||
except Exception as e:
|
||||
logger.debug(f"HTML 수정 버튼 요소를 찾을 수 없습니다. : {e}", exc_info=True)
|
||||
|
||||
|
|
@ -507,62 +524,78 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
|
||||
|
||||
try:
|
||||
# textarea_css = "ck-source-editing-area"
|
||||
# click_element(driver, 'CSS_SELECTOR', textarea_css, 10, 'js')
|
||||
textarea = WebDriverWait(driver, 5).until(
|
||||
EC.presence_of_element_located((By.CLASS_NAME, "ck-source-editing-area"))
|
||||
)
|
||||
logger.debug("textarea버튼을 성공적으로 찾았습니다.")
|
||||
sys.stdout.flush()
|
||||
textarea.click()
|
||||
time.sleep(0.2)
|
||||
actions = ActionChains(driver)
|
||||
actions.move_to_element(textarea).perform() # 요소로 스크롤
|
||||
logger.debug("'textarea' 요소로 스크롤 실행")
|
||||
time.sleep(0.2)
|
||||
actions.click(textarea).perform()
|
||||
logger.debug("'textarea' 요소 클릭 실행")
|
||||
|
||||
# textarea.click()
|
||||
except Exception as e:
|
||||
logger.debug(f"textarea버튼 요소를 찾을 수 없습니다. : {e}", exc_info=True)
|
||||
|
||||
try:
|
||||
# # data-value 속성 값을 가져옵니다.
|
||||
current_value = textarea.get_attribute("data-value")
|
||||
original_html_tags = original_html(current_value)
|
||||
# logger.debug(f"현재 속성값 :{current_value}")
|
||||
product_info.current_value = current_value
|
||||
product_info.current_value = original_html_tags
|
||||
|
||||
logger.debug("현재 속성값 수집 완료")
|
||||
|
||||
# # ============번역을 위한 사전작업============
|
||||
# 이미지 Url 수집
|
||||
# translated_image_urls = [] # 번역된 이미지 URL들을 저장할 리스트
|
||||
detail_images = fetch_image_urls(current_value)
|
||||
logger.debug(f"detail_images URLs \n {detail_images}")
|
||||
detail_images = fetch_image_urls(original_html_tags)
|
||||
# logger.debug(f"detail_images URLs \n {detail_images}")
|
||||
product_info.detail_image_urls = detail_images
|
||||
|
||||
|
||||
# 원본 URL을 빈문자열로 대체
|
||||
logger.debug("원본 URL을 빈 문자열로 대체")
|
||||
for original_url in detail_images:
|
||||
current_value = current_value.replace(original_url, "")
|
||||
# logger.debug("원본 URL을 빈 문자열로 대체")
|
||||
# for original_url in detail_images:
|
||||
# current_value = current_value.replace(original_url, "")
|
||||
# logger.debug("새로운 data-value 값으로 설정되었습니다.")
|
||||
# # ============번역을 위한 사전작업============
|
||||
|
||||
|
||||
# # # 새로운 data-value 값을 생성합니다.
|
||||
# new_value = product_info_text + "\n" + current_value
|
||||
# # logger.debug(f"new_value \n {new_value}")
|
||||
# product_info.new_value = new_value
|
||||
# logger.debug("새로운 data-value 값 생성")
|
||||
|
||||
# # # data-value 속성을 새로운 값으로 설정합니다.
|
||||
# driver.execute_script("arguments[0].setAttribute('data-value', arguments[1]);", textarea, new_value)
|
||||
|
||||
# # driver.execute_script("arguments[0].innerHTML = arguments[1]", textarea, new_value)
|
||||
# time.sleep(1)
|
||||
# # driver.execute_script("arguments[0].innerHTML = arguments[1]", detail_content, new_value);
|
||||
|
||||
logger.debug("새로운 data-value 값으로 설정되었습니다.")
|
||||
|
||||
# save_button.click()
|
||||
click_element(driver, 'XPATH', save_button_xpath, 5, 'js')
|
||||
logger.debug("product_info_card 결합.")
|
||||
new_value = product_info_card +'<br><br>' + cost_add_text +'<br><br>' +original_html_tags
|
||||
# textarea.send_keys(product_info_card)
|
||||
time.sleep(0.2)
|
||||
|
||||
logger.debug("결합.")
|
||||
# 결합된 값을 다시 textarea에 설정
|
||||
try:
|
||||
# # data-value 속성을 새로운 값으로 설정합니다.
|
||||
driver.execute_script("arguments[0].setAttribute('data-value', arguments[1]);", textarea, new_value)
|
||||
|
||||
# textarea.send_keys(product_info_text)
|
||||
# logger.debug("detail_content 전송.")
|
||||
# # driver.execute_script("arguments[0].innerHTML = arguments[1]", textarea, new_value)
|
||||
# # driver.execute_script("arguments[0].innerHTML = arguments[1]", detail_content, new_value);
|
||||
|
||||
# # JavaScript를 사용하여 textarea 값 설정
|
||||
# script = "arguments[0].value = arguments[1];"
|
||||
# driver.execute_script(script, textarea, new_value)
|
||||
time.sleep(0.5)
|
||||
logger.debug("새로운 값이 성공적으로 설정됨.")
|
||||
except Exception as e:
|
||||
logger.error(f"새로운 값 설정 중 오류 발생: {e}", exc_info=True)
|
||||
time.sleep(0.2)
|
||||
|
||||
# textarea.send_keys(Keys.ENTER)
|
||||
# textarea.send_keys(Keys.ENTER)
|
||||
# logger.debug("엔터키 전송.")
|
||||
|
||||
click_element(driver, 'XPATH', save_button_xpath, 5, 'js')
|
||||
time.sleep(0.2)
|
||||
logger.debug("저장버튼 전송.")
|
||||
|
||||
# html_btn.click()
|
||||
click_element(driver, 'XPATH', html_btn_xpath, 5, 'js')
|
||||
|
||||
|
|
@ -588,6 +621,8 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
# contents 변수의 값이 None이 아닐 때만 send_keys 메서드를 호출합니다.
|
||||
if aicontents is not None:
|
||||
detail_content.send_keys(aicontents)
|
||||
logger.debug(f"AI 컨텐츠 입력완료")
|
||||
|
||||
else:
|
||||
# contents 변수가 None일 때의 대체 처리
|
||||
# 예: detail_content.send_keys("") 또는 아무 동작도 수행하지 않음
|
||||
|
|
@ -602,6 +637,9 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
logger.error(f"AI 컨텐츠 입력 중 에러발생 : {e}", exc_info=True)
|
||||
|
||||
|
||||
|
||||
|
||||
if login_info.whether_modifyImageTanslation:
|
||||
logger.debug("상세페이지 이미지 번역 시작")
|
||||
try:
|
||||
for i, detail_image in enumerate(detail_images):
|
||||
|
|
@ -621,74 +659,11 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
except Exception as e:
|
||||
logger.error(f"이미지 번역 중 에러발생 : {e}", exc_info=True)
|
||||
|
||||
|
||||
logger.debug("====번역이미지 붙여넣기 완료=====")
|
||||
|
||||
# 인용버튼 클릭
|
||||
# quote_button = driver.find_element(By.XPATH, "//div[@id='productMainContentContainerId']/div/div/div[2]/div[2]/div/div/div[2]/div/div/button[6]")
|
||||
|
||||
# quote_button = driver.find_element(By.CSS_SELECTOR, ".ck:nth-child(16)")
|
||||
|
||||
# try:
|
||||
# quote_button = WebDriverWait(driver, 10).until(
|
||||
# EC.presence_of_element_located((By.CSS_SELECTOR, ".ck:nth-child(16)"))
|
||||
# )
|
||||
# logger.debug("인용버튼을 성공적으로 찾았습니다.")
|
||||
# except:
|
||||
# logger.debug("인용버튼 요소를 찾을 수 없습니다.")
|
||||
|
||||
# quote_button.click()
|
||||
# time.sleep(0.5)
|
||||
|
||||
try:
|
||||
# 가격 정보 계산
|
||||
low_cost, low_margin = selling_price(product_low_cost, delv_fee, 0.29)
|
||||
high_cost, high_margin = selling_price(product_high_cost, delv_fee, 0.29)
|
||||
low_delv_fee = avg_price - low_cost
|
||||
high_delv_fee = avg_price - high_cost
|
||||
|
||||
# 마진율 계산
|
||||
low_margin_r = round((low_margin / low_cost) * 100, 2)
|
||||
high_margin_r = round((high_margin / high_cost) * 100, 2)
|
||||
|
||||
# 텍스트 조합
|
||||
add_text = f"""
|
||||
============= 가격과 마진율 =============
|
||||
해당 제품의 네이버 평균가격은 {avg_price:,.0f}원입니다.
|
||||
해당 제품의 무게배송비는 {delv_fee:,.0f}원입니다.
|
||||
|
||||
**마진율 확보를 위한 판매가 범위:** {low_cost:,.0f}원 ~ {high_cost:,.0f}원
|
||||
|
||||
**저가:**
|
||||
* 무게 배송비 대비: {low_delv_fee:,.0f}원 {('낮은' if low_delv_fee < 0 else '높은')} 편
|
||||
* 마진율: {low_margin_r:,.2f}%
|
||||
|
||||
**고가:**
|
||||
* 무게 배송비 대비: {high_delv_fee:,.0f}원 {('낮은' if high_delv_fee < 0 else '높은')} 편
|
||||
* 마진율: {high_margin_r:,.2f}%
|
||||
|
||||
**적정 배송비:** {low_delv_fee:,.0f}원 ~ {high_delv_fee:,.0f}원
|
||||
|
||||
**추가 정보:**
|
||||
* 해당 제품의 파손 여부 및 무게 오차를 고려하여 더하기 마진을 설정하세요.
|
||||
=======================================
|
||||
"""
|
||||
|
||||
detail_content.send_keys(add_text)
|
||||
time.sleep(2)
|
||||
except Exception as e:
|
||||
logger.debug(f"가격정보 생성 중 에러 : {e}", exc_info=True)
|
||||
|
||||
# 저장 버튼 클릭
|
||||
# try:
|
||||
# save_button = WebDriverWait(driver, 10).until(
|
||||
# EC.presence_of_element_located((By.XPATH, "//button[contains(.,'저장하기')]"))
|
||||
# )
|
||||
# logger.debug("저장 버튼을 성공적으로 찾았습니다.")
|
||||
# except Exception as e:
|
||||
# logger.debug(f"저장 버튼 요소를 찾을 수 없습니다. : {e}", exc_info=True)
|
||||
# save_button = driver.find_element(By.XPATH, "//button[contains(.,'저장하기')]")
|
||||
# save_button.click()
|
||||
|
||||
logger.debug("상세페이지 편집 저장")
|
||||
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
from playwright.sync_api import sync_playwright
|
||||
from bs4 import BeautifulSoup
|
||||
import requests, json
|
||||
|
||||
def run(playwright, keyword):
|
||||
browser = playwright.chromium.launch(headless=False) # 헤드리스 모드 비활성화
|
||||
context = browser.new_context(
|
||||
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36",
|
||||
locale="en-US", # 언어 설정
|
||||
timezone_id="America/New_York" # 시간대 설정
|
||||
)
|
||||
page = context.new_page()
|
||||
|
||||
# navigator.webdriver 비활성화
|
||||
page.evaluate("navigator.webdriver = undefined")
|
||||
|
||||
print("브라우저 열기 및 사용자 에이전트 설정 완료")
|
||||
|
||||
page.goto(f"https://search.shopping.naver.com/search/all?query={keyword}&bt=-1&frm=NVSCPRO")
|
||||
print("페이지 로딩 완료")
|
||||
|
||||
# 접근 시간 지연
|
||||
page.wait_for_timeout(1000)
|
||||
|
||||
print("페이지 로딩 완료")
|
||||
# 페이지의 HTML 가져오기
|
||||
html = page.content()
|
||||
print(html)
|
||||
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
|
||||
products = extract_product_info(soup)
|
||||
|
||||
for product in products:
|
||||
print(product.text)
|
||||
|
||||
browser.close()
|
||||
|
||||
|
||||
def extract_product_info(soup):
|
||||
# 상품 정보를 담을 리스트 초기화
|
||||
product_info_list = []
|
||||
|
||||
# 각 상품에 대한 정보 추출
|
||||
products = soup.find_all('div', class_='basicList_info_area__17Xyo')
|
||||
print(f"products : {products}")
|
||||
for i, product in enumerate(products, 1):
|
||||
# 상품명
|
||||
title = product.find('a', class_='product_link__TrAac').get('title')
|
||||
print(f"{i}번째 상품 title : {title}")
|
||||
|
||||
# 썸네일 URL
|
||||
thumbnail_element = product.find('a', class_='thumbnail_thumb__Bxb6Z')
|
||||
thumbnail_url = thumbnail_element.find('img').get('src') if thumbnail_element else None
|
||||
print(f"{i}번째 상품 thumbnail_url : {thumbnail_url}")
|
||||
|
||||
# 가격과 배송비
|
||||
price_area = product.find('div', class_='product_price_area__eTg7I')
|
||||
price = price_area.find('span', class_='price_num__S2p_v').text.strip() if price_area else None
|
||||
print(f"{i}번째 상품 price : {price}")
|
||||
delivery_fee = price_area.find('span', class_='price_delivery__yw_We').text.strip() if price_area else None
|
||||
print(f"{i}번째 상품 delivery_fee : {delivery_fee}")
|
||||
|
||||
# 카테고리
|
||||
categories = [cat.text for cat in product.find_all('span', class_='product_category__l4FWz')]
|
||||
print(f"{i}번째 상품 categories : {categories}")
|
||||
|
||||
# 리뷰, 구매건수, 등록일, 찜하기
|
||||
etc_info = product.find('div', class_='product_etc_box__ElfVA')
|
||||
review_count = etc_info.find('a', text='리뷰').find('em').text if etc_info and etc_info.find('a', text='리뷰') else '0'
|
||||
print(f"{i}번째 상품 review_count : {review_count}")
|
||||
purchase_count = etc_info.find('span', text='구매건수').find('em').text if etc_info and etc_info.find('span', text='구매건수') else '0'
|
||||
print(f"{i}번째 상품 purchase_count : {purchase_count}")
|
||||
registration_date = etc_info.find('span', text='등록일').text.replace('등록일 ', '') if etc_info and etc_info.find('span', text='등록일') else None
|
||||
print(f"{i}번째 상품 registration_date : {registration_date}")
|
||||
zzim_count = etc_info.find('a', class_='product_btn_zzim__MQ17u').find('em').text if etc_info and etc_info.find('a', class_='product_btn_zzim__MQ17u') else '0'
|
||||
print(f"{i}번째 상품 zzim_count : {zzim_count}")
|
||||
|
||||
# 사전에 정보 저장
|
||||
product_info = {
|
||||
'title': title,
|
||||
'thumbnail_url': thumbnail_url,
|
||||
'price': price,
|
||||
'delivery_fee': delivery_fee,
|
||||
'categories': categories,
|
||||
'review_count': review_count,
|
||||
'purchase_count': purchase_count,
|
||||
'registration_date': registration_date,
|
||||
'zzim_count': zzim_count
|
||||
}
|
||||
|
||||
product_info_list.append(product_info)
|
||||
print(f"{i}번째 상품 정보 추가 완료")
|
||||
|
||||
return product_info_list
|
||||
|
||||
|
||||
|
||||
def ns(keyword):
|
||||
# 네이버 쇼핑 URL 설정
|
||||
urlBase = "https://search.shopping.naver.com/search/all?query="
|
||||
# urlEnd = f"&cat_id={naver_code}&frm=NVSHATC&pagingIndex=1&pagingSize=40&&productSet=overseas&sort=rel×tamp=&viewType=list"
|
||||
urlEnd = "&frm=NVSHATC&pagingIndex=1&pagingSize=40&&productSet=overseas&sort=rel×tamp=&viewType=list"
|
||||
url = urlBase + keyword + urlEnd
|
||||
# print(f"네이버 카테코드는 [{naver_code}] 입니다.")
|
||||
print("네이버 카테코드는 [생략]] 입니다.")
|
||||
print(f"대상키워드는 [{keyword}] 입니다.")
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
|
||||
"Accept-Language": "en-US,en;q=0.9",
|
||||
"Accept-Encoding": "gzip, deflate, br",
|
||||
"DNT": "1", # Do Not Track 요청 헤더 (사용자의 추적을 거부)
|
||||
"Connection": "keep-alive",
|
||||
"Upgrade-Insecure-Requests": "1", # https로의 업그레이드를 요청
|
||||
"Cache-Control": "max-age=0", # 캐시된 콘텐츠를 재사용하지 않도록 요청
|
||||
}
|
||||
|
||||
# # 네이버 쇼핑에 접속하여 HTML 받아오기
|
||||
# response = requests.get(url, headers=headers)
|
||||
# soup = BeautifulSoup(response.text, 'html.parser')
|
||||
|
||||
# 네이버 쇼핑에 접속하여 HTML 받아오기
|
||||
try:
|
||||
response = requests.get(url, headers=headers)
|
||||
html = response.text
|
||||
print(f"response : {response}")
|
||||
# print(f"html : {html}")
|
||||
response.raise_for_status() # 만약 요청이 실패하면 예외 발생
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
next_data_str = soup.find("script", {"id": "__NEXT_DATA__"}).string
|
||||
next_data_json = json.loads(next_data_str)
|
||||
products_list = next_data_json["props"]["pageProps"]["initialState"]["products"]["list"]
|
||||
products_info = []
|
||||
for product in products_list:
|
||||
price = product.get("item", {}).get("price")
|
||||
productTitle = product.get("item", {}).get("productTitle")
|
||||
category1Name = product.get("item", {}).get("category1Name")
|
||||
category2Name = product.get("item", {}).get("category2Name")
|
||||
category3Name = product.get("item", {}).get("category3Name")
|
||||
category4Name = product.get("item", {}).get("category4Name")
|
||||
openDate = product.get("item", {}).get("openDate")
|
||||
mallCount = product.get("item", {}).get("mallCount")
|
||||
keepCnt = product.get("item", {}).get("keepCnt")
|
||||
overseaTp = product.get("item", {}).get("overseaTp")
|
||||
reviewCount = product.get("item", {}).get("reviewCount")
|
||||
reviewCountSum = product.get("item", {}).get("reviewCountSum")
|
||||
scoreInfo = product.get("item", {}).get("scoreInfo")
|
||||
naverPayAdAccumulatedDisplayValue = product.get("item", {}).get("naverPayAdAccumulatedDisplayValue")
|
||||
mobileLowPrice = product.get("item", {}).get("mobileLowPrice")
|
||||
lowPrice = product.get("item", {}).get("lowPrice")
|
||||
deliveryFeeContent = product.get("item", {}).get("deliveryFeeContent")
|
||||
dlvryLowPrice = product.get("item", {}).get("dlvryLowPrice")
|
||||
imageUrl = product.get("item", {}).get("imageUrl")
|
||||
imgSz = product.get("item", {}).get("imgSz")
|
||||
searchKeyword = product.get("item", {}).get("searchKeyword")
|
||||
mallProductUrl = product.get("item", {}).get("mallProductUrl")
|
||||
mallPcUrl = product.get("item", {}).get("mallPcUrl")
|
||||
mallName = product.get("item", {}).get("mallName")
|
||||
manuTag = product.get("item", {}).get("manuTag")
|
||||
#mallInfoCache = product.get("item", {}).get("mallInfoCache")
|
||||
purchaseCnt = product.get("item", {}).get("purchaseCnt")
|
||||
rank = product.get("item", {}).get("rank")
|
||||
|
||||
# 상품 정보를 딕셔너리로 만들어 리스트에 추가
|
||||
product_info = {
|
||||
"productTitle": productTitle,
|
||||
"price": price,
|
||||
"imageUrl": imageUrl,
|
||||
"rank": rank,
|
||||
"purchase" : purchaseCnt,
|
||||
"review" : reviewCountSum
|
||||
}
|
||||
products_info.append(product_info)
|
||||
|
||||
print(f"키워드 검색 결과 상품 [{keyword}]에 대한 [{len(products_info)}]개의 상품정보수집 완료")
|
||||
|
||||
print(f"products_info \n {products_info}")
|
||||
except Exception as e:
|
||||
print(f"Exception : {e}")
|
||||
|
||||
|
||||
keywrod = "방폭등"
|
||||
|
||||
ns(keywrod)
|
||||
|
||||
# with sync_playwright() as playwright:
|
||||
# run(playwright, keywrod)
|
||||
|
|
@ -6,7 +6,7 @@ from selenium.webdriver.common.keys import Keys
|
|||
import time
|
||||
from edit.action_elements import click_element, return_element, click_and_confirm_tab
|
||||
from ai.deepl import trans
|
||||
from ai.deepl_with_playwright import trans_text
|
||||
from ai.deepl_with_playwright import trans_text, trans_list_text
|
||||
# from ai.compare import find_most_similar_image_by_one
|
||||
import re
|
||||
import logging
|
||||
|
|
@ -251,31 +251,35 @@ def option_name_trans(driver, product_info, option_type_number, option_count, al
|
|||
|
||||
ori_optionName_element = driver.find_element(By.XPATH, ori_optionName_xpath)
|
||||
ori_optionName = ori_optionName_element.text.strip()
|
||||
|
||||
ori_optionNames.append(ori_optionName)
|
||||
cleaned_ori_optionName = replace_or_remove_special_chars(ori_optionName, allowed_special_chars, special_char_replacements)
|
||||
ori_optionNames.append(cleaned_ori_optionName)
|
||||
except Exception as e:
|
||||
logger.debug(f"원본옵션명 처리중 에러발생 : {e}", exc_info=True)
|
||||
|
||||
|
||||
# 원본 옵션명을 하나의 텍스트로 합치기
|
||||
combined_ori_optionNames = '\n\n'.join(ori_optionNames)
|
||||
# combined_ori_optionNames = '\n\n'.join(ori_optionNames)
|
||||
|
||||
logger.debug("원본 텍스트의 특수문자 제거 및 대체")
|
||||
cleaned_ori_text = replace_or_remove_special_chars(combined_ori_optionNames, allowed_special_chars, special_char_replacements)
|
||||
|
||||
logger.debug(f"원본옵션명 집합 \n {cleaned_ori_text}")
|
||||
# logger.debug("원본 텍스트의 특수문자 제거 및 대체")
|
||||
# cleaned_ori_text = replace_or_remove_special_chars(combined_ori_optionNames, allowed_special_chars, special_char_replacements)
|
||||
# logger.debug(f"원본옵션명 집합 \n {cleaned_ori_text}")
|
||||
|
||||
logger.debug("번역 시행")
|
||||
# trans_optionNames_text = trans_text(cleaned_ori_text) # DeepL 번역 함수
|
||||
trans_optionNames_text = trans(cleaned_ori_text) # DeepL 번역 함수
|
||||
deepl_trans_optionNames = trans_list_text(ori_optionNames) # DeepL 번역 함수
|
||||
# trans_optionNames_text = trans_text(cleaned_ori_text) # DeepL_with_playwright 번역 함수
|
||||
|
||||
logger.debug(f"번역된 텍스트 \n {trans_optionNames_text} ")
|
||||
# trans_optionNames_text = trans(cleaned_ori_text) # DeepL 번역 함수
|
||||
|
||||
logger.debug(f"번역된 텍스트 \n {deepl_trans_optionNames} ")
|
||||
|
||||
logger.debug("번역 텍스트의 특수문자 제거 및 대체")
|
||||
cleaned_ori_text = replace_or_remove_special_chars(trans_optionNames_text, allowed_special_chars, special_char_replacements)
|
||||
trans_optionNames = []
|
||||
for deepl_trans_optionName in deepl_trans_optionNames:
|
||||
cleand_trans_optionName = replace_or_remove_special_chars(deepl_trans_optionName, allowed_special_chars, special_char_replacements)
|
||||
trans_optionNames.append(cleand_trans_optionName)
|
||||
|
||||
trans_optionNames = trans_optionNames_text.split('\n\n')
|
||||
logger.debug("번역 텍스트 나누기")
|
||||
# trans_optionNames = trans_optionNames_text.split('\n\n')
|
||||
# logger.debug("번역 텍스트 나누기")
|
||||
|
||||
logger.debug("product_info 옵션명 업데이트")
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -20,14 +20,16 @@ def modify_price_page(driver, product_infos):
|
|||
|
||||
# 가격 탭으로 이동
|
||||
logger.debug("가격탭으로 이동")
|
||||
option_tab_XPATH = "//div[@id='rc-tabs-0-tab-3']"
|
||||
keyword_tab_CSS = ".ant-tabs-tab:nth-child(3)"
|
||||
click_element(driver, 'CSS_SELECTOR', keyword_tab_CSS, 10, 'js')
|
||||
# option_tab_XPATH = "//div[@id='rc-tabs-0-tab-3']"
|
||||
# keyword_tab_CSS = ".ant-tabs-tab:nth-child(3)"
|
||||
# click_element(driver, 'CSS_SELECTOR', keyword_tab_CSS, 10, 'js')
|
||||
logger.debug("가격탭으로 이동 완료")
|
||||
|
||||
logger.debug("페이지 로딩 대기")
|
||||
|
||||
time.sleep(2) # 페이지 로딩 대기.
|
||||
|
||||
try:
|
||||
option_high_price = product_infos.option_high_price
|
||||
option_low_price = product_infos.option_low_price
|
||||
|
||||
|
|
@ -49,8 +51,10 @@ def modify_price_page(driver, product_infos):
|
|||
"ns_high_price": naver_high_price
|
||||
}
|
||||
logger.debug(f"가격계산 INPUT 요소 : {inputs}")
|
||||
except Exception as e:
|
||||
logger.error(f"가격계산 INPUT 요소 계산 중 에러 발생 {e}", exc_info=True)
|
||||
|
||||
|
||||
try:
|
||||
outputs = calculate_margin_and_price(inputs)
|
||||
logger.debug(f"가격계산 OUTPUT 요소 : {outputs}")
|
||||
|
||||
|
|
@ -60,7 +64,7 @@ def modify_price_page(driver, product_infos):
|
|||
plus_margin = outputs["plus_margin"]
|
||||
ns_avg_price = outputs["ns_avg_price"]
|
||||
|
||||
return_fee = selling_price/2
|
||||
return_fee = round((selling_price*0.4) / 1000) * 1000
|
||||
|
||||
init_delv_fee = seller_cost
|
||||
exchange_fee = return_fee + init_delv_fee
|
||||
|
|
@ -70,21 +74,24 @@ def modify_price_page(driver, product_infos):
|
|||
product_infos.init_delv_fee = init_delv_fee
|
||||
product_infos.exchange_fee = exchange_fee
|
||||
|
||||
print(f"결정된 상품가격: {selling_price}원, 최종 마진율: {final_margin_rate:.2f}%")
|
||||
print(f"판매자 원가: {seller_cost}원")
|
||||
print(f"더하기 마진: {plus_margin}원")
|
||||
print(f"네이버 평균가: {ns_avg_price}원")
|
||||
logger.debug(f"결정된 상품가격: {selling_price}원, 최종 마진율: {final_margin_rate:.2f}%")
|
||||
logger.debug(f"판매자 원가: {seller_cost}원")
|
||||
logger.debug(f"더하기 마진: {plus_margin}원")
|
||||
logger.debug(f"네이버 평균가: {ns_avg_price}원")
|
||||
except Exception as e:
|
||||
logger.error(f"가격계산 OUTPUT 요소 계산 중 에러 발생 {e}", exc_info=True)
|
||||
|
||||
|
||||
|
||||
# 가격 탭으로 이동
|
||||
logger.debug("내부함수 가격 탭으로 이동")
|
||||
thumb_tab_CSS = '.ant-tabs-tab:nth-child(3)'
|
||||
click_element(driver, 'CSS_SELECTOR', thumb_tab_CSS, 10, 'js')
|
||||
logger.debug("내부함수 가격 탭으로 이동 완료")
|
||||
logger.debug("페이지 로딩 대기")
|
||||
time.sleep(2) # 페이지 로딩 대기.
|
||||
# # 가격 탭으로 이동
|
||||
# logger.debug("내부함수 가격 탭으로 이동")
|
||||
# thumb_tab_CSS = '.ant-tabs-tab:nth-child(3)'
|
||||
# click_element(driver, 'CSS_SELECTOR', thumb_tab_CSS, 10, 'js')
|
||||
# logger.debug("내부함수 가격 탭으로 이동 완료")
|
||||
# logger.debug("페이지 로딩 대기")
|
||||
# time.sleep(2) # 페이지 로딩 대기.
|
||||
|
||||
try:
|
||||
logger.debug("더하기마진 수정")
|
||||
plus_fee_xpath="//div[@id='productMainContentContainerId']/div/div/div/div/div[2]/div/div/div[8]/div/div/div[3]/div/div/div/div/div[2]/input"
|
||||
plus_fee_element = return_element(driver, 'XPATH', plus_fee_xpath, 10)
|
||||
|
|
@ -93,7 +100,10 @@ def modify_price_page(driver, product_infos):
|
|||
logger.debug("기존가격 삭제")
|
||||
plus_fee_element.send_keys(plus_margin) # 새 가격 입력
|
||||
logger.debug(f"더하기마진 수정 완료 : {plus_margin}")
|
||||
except Exception as e:
|
||||
logger.error(f"더하기마진 수정 중 에러 발생 {e}", exc_info=True)
|
||||
|
||||
try:
|
||||
logger.debug("해외배송비 수정")
|
||||
fore_delv_fee_xpath="//div[@id='productMainContentContainerId']/div/div/div/div/div[2]/div/div/div[10]/div/div/div/div/div[2]/input"
|
||||
fore_delv_fee_element = return_element(driver, 'XPATH', fore_delv_fee_xpath, 10)
|
||||
|
|
@ -102,7 +112,10 @@ def modify_price_page(driver, product_infos):
|
|||
logger.debug("기존가격 삭제")
|
||||
fore_delv_fee_element.send_keys(w_delv_fee) # 새 가격 입력
|
||||
logger.debug(f"해외배송비 수정 완료 : {w_delv_fee}")
|
||||
except Exception as e:
|
||||
logger.error(f"해외배송비 수정 중 에러 발생 {e}", exc_info=True)
|
||||
|
||||
try:
|
||||
logger.debug("반품비 수정")
|
||||
return_fee_xpath = "//div[@id='productMainContentContainerId']/div/div/div/div/div[4]/div/div/div[3]/div/div/div/div/div[2]/input"
|
||||
return_fee_element = return_element(driver, 'XPATH', return_fee_xpath, 10)
|
||||
|
|
@ -111,7 +124,10 @@ def modify_price_page(driver, product_infos):
|
|||
logger.debug("기존가격 삭제")
|
||||
return_fee_element.send_keys(return_fee) # 새 가격 입력
|
||||
logger.debug(f"반품비 수정 완료 : {return_fee}")
|
||||
except Exception as e:
|
||||
logger.error(f"반품비 수정 중 에러 발생 {e}", exc_info=True)
|
||||
|
||||
try:
|
||||
logger.debug("초도배송비 수정")
|
||||
first_delv_fee_xpath = "//div[@id='productMainContentContainerId']/div/div/div/div/div[4]/div/div/div[4]/div/div[2]/div/div/div[2]/input"
|
||||
first_delv_fee_element = return_element(driver, 'XPATH', first_delv_fee_xpath, 10)
|
||||
|
|
@ -120,7 +136,10 @@ def modify_price_page(driver, product_infos):
|
|||
logger.debug("기존가격 삭제")
|
||||
first_delv_fee_element.send_keys(init_delv_fee) # 새 가격 입력
|
||||
logger.debug(f"초도배송비 수정 완료 : {init_delv_fee}")
|
||||
except Exception as e:
|
||||
logger.error(f"초도배송비 수정 중 에러 발생 {e}", exc_info=True)
|
||||
|
||||
try:
|
||||
logger.debug("교환비 수정")
|
||||
exchange_fee_xpath = "//div[@id='productMainContentContainerId']/div/div/div/div/div[4]/div/div/div[5]/div/div/div/div/div[2]/input"
|
||||
exchange_fee_element = return_element(driver, 'XPATH', exchange_fee_xpath, 10)
|
||||
|
|
@ -128,7 +147,9 @@ def modify_price_page(driver, product_infos):
|
|||
driver.execute_script("arguments[0].value = '';", exchange_fee_element) # 기존 가격 삭제 JS
|
||||
logger.debug("기존가격 삭제")
|
||||
exchange_fee_element.send_keys(exchange_fee) # 새 가격 입력
|
||||
logger.debug(f"교환비 수정 완료 : {exchange_fee}")
|
||||
logger.debug(f"교환비 수정 완료 : {exchange_fee}`")
|
||||
except Exception as e:
|
||||
logger.error(f"교환비 수정 중 에러 발생 {e}", exc_info=True)
|
||||
|
||||
# logger.debug("기본마진율 수정")
|
||||
# earning_rate_xpath = "//div[@id='productMainContentContainerId']/div/div/div/div/div[2]/div/div/div[8]/div/div/div/div/div/div/div/div[2]/input"
|
||||
|
|
@ -142,6 +163,9 @@ def modify_price_page(driver, product_infos):
|
|||
# 백그라운드 컬러로 빨간 가격탭 찾기
|
||||
# background: rgb(255, 77, 79); box-shadow: none; color: rgb(255, 255, 255);
|
||||
|
||||
try:
|
||||
save_xpath="//button[contains(.,'저장하기')]"
|
||||
click_element(driver, 'XPATH', save_xpath, 10)
|
||||
logger.debug("옵션 정리 후 저장버튼 클릭 완료")
|
||||
logger.debug("가격 정리 후 저장버튼 클릭 완료")
|
||||
except Exception as e:
|
||||
logger.error(f"가격 정리 저장버튼 클릭 중 에러 발생 {e}", exc_info=True)
|
||||
|
|
@ -16,12 +16,14 @@ def calculate_margin_and_price(inputs):
|
|||
plus_margin = 5000 # 기본값
|
||||
|
||||
# 계산 로직
|
||||
ns_avg_price = (ns_low_price + ns_high_price) / 2
|
||||
ns_avg_price = round(((ns_low_price + ns_high_price) / 2) / 100) * 100
|
||||
logger.debug(f"ns_avg_price : {ns_avg_price}")
|
||||
option_avg_price = (option_low_price + option_high_price) / 2
|
||||
option_avg_price = round(((option_low_price + option_high_price) / 2) / 100) * 100
|
||||
logger.debug(f"option_avg_price : {option_avg_price}")
|
||||
tao_cost = lambda price: price * 190 * 1.035
|
||||
base_margin = lambda cost: cost * 0.12
|
||||
# tao_cost = lambda price: price * 190 * 1.035
|
||||
tao_cost = lambda price: round(price * 1.035 / 100) * 100
|
||||
base_margin = lambda cost: round(cost * 0.12 / 100) * 100
|
||||
|
||||
|
||||
def adjust_plus_margin_for_min_margin(seller_cost, mall_cost, selling_price):
|
||||
nonlocal plus_margin
|
||||
|
|
@ -32,16 +34,18 @@ def calculate_margin_and_price(inputs):
|
|||
if final_margin_rate < target_margin_rate:
|
||||
plus_margin += 100 # 더하기 마진 증가
|
||||
selling_price = seller_cost + plus_margin + mall_cost
|
||||
mall_cost = selling_price * 0.13
|
||||
mall_cost = round(selling_price * 0.13 / 100) * 100
|
||||
else:
|
||||
break
|
||||
|
||||
logger.debug(f"final_margin_rate : {final_margin_rate}")
|
||||
|
||||
return selling_price, final_margin_rate
|
||||
|
||||
seller_cost = tao_cost(option_avg_price) + delv_fee + packing_fee
|
||||
logger.debug(f"seller_cost : {seller_cost}")
|
||||
|
||||
mall_cost = seller_cost * 0.13
|
||||
mall_cost = round((seller_cost * 0.13)/100) * 100
|
||||
selling_price = seller_cost + plus_margin + mall_cost
|
||||
selling_price, final_margin_rate = adjust_plus_margin_for_min_margin(seller_cost, mall_cost, selling_price)
|
||||
|
||||
|
|
|
|||
|
|
@ -68,8 +68,10 @@ def image_trans(image_url, convert_type):
|
|||
# 번역된 텍스트 추출 및 삽입
|
||||
texts_to_translate = [text for _, text, _, _, _ in detected_texts]
|
||||
logger.debug(f"원본 텍스트: {texts_to_translate}")
|
||||
translated_texts = translate_texts_translatepy(texts_to_translate)
|
||||
logger.debug(f"DEEPL 번역시작")
|
||||
translated_texts = translate_texts_deepl(texts_to_translate)
|
||||
if not translated_texts: # 빈 리스트 체크
|
||||
logger.debug(f"DEEPL 번역실패로 translatepy 시작")
|
||||
translated_texts = translate_texts_translatepy(texts_to_translate)
|
||||
logger.debug(f"번역 텍스트: {texts_to_translate}")
|
||||
# translated_image = insert_text(inpainted_image_path, translated_texts, detected_texts)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from googletrans import Translator
|
||||
# from img_trans.src.deepl_ori import trans
|
||||
from ai.deepl import trans
|
||||
from ai.deepl_with_playwright import trans_list_text, trans_text
|
||||
from translatepy import Translator
|
||||
import logging
|
||||
|
||||
|
|
@ -21,7 +22,7 @@ def translate_texts_google(texts, src_lang='zh-cn', dest_lang='ko'):
|
|||
return translated_texts
|
||||
|
||||
|
||||
def translate_texts_deepl(texts):
|
||||
def translate_texts_deepl_ori(texts):
|
||||
"""텍스트 리스트를 지정된 언어에서 다른 언어로 번역"""
|
||||
# 텍스트 리스트를 엔터로 구분된 하나의 문자열로 합침
|
||||
combined_text = "\n".join(texts)
|
||||
|
|
@ -29,18 +30,34 @@ def translate_texts_deepl(texts):
|
|||
|
||||
# DeepL로 전체 텍스트 번역
|
||||
try:
|
||||
translated_combined_text = trans(combined_text)
|
||||
# translated_combined_text = trans(combined_text)
|
||||
translated_combined_text = trans_list_text(combined_text)
|
||||
for translation in translated_combined_text:
|
||||
# "가격"이 포함된 경우 빈 문자열로 대체
|
||||
translated_texts.append(translation.text if "가격" not in translation.text else "")
|
||||
# 번역된 전체 텍스트를 엔터를 기준으로 분리하여 리스트로 만듦
|
||||
translated_texts = translated_combined_text.split("\n")
|
||||
except AttributeError as e:
|
||||
logger.error(f"번역 실패: {e}")
|
||||
logger.error(f"번역 실패: {e}", exc_info=True)
|
||||
translated_texts = []
|
||||
|
||||
return translated_texts
|
||||
|
||||
def translate_texts_deepl(texts):
|
||||
"""텍스트 리스트를 지정된 언어에서 다른 언어로 번역"""
|
||||
translated_texts = []
|
||||
|
||||
# DeepL로 전체 텍스트 번역
|
||||
try:
|
||||
translated_texts = trans_list_text(texts)
|
||||
# "가격"이 포함된 경우 빈 문자열로 대체
|
||||
translated_texts = [text if "가격" not in text else "" for text in translated_texts]
|
||||
|
||||
except AttributeError as e:
|
||||
logger.error(f"번역 실패: {e}", exc_info=True)
|
||||
translated_texts = []
|
||||
|
||||
return translated_texts
|
||||
|
||||
def translate_texts_translatepy(texts, src_lang='zh-cn', dest_lang='ko'):
|
||||
"""translatepy 라이브러리를 사용하여 텍스트 리스트를 번역합니다."""
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class LoginWidget(QtWidgets.QWidget):
|
|||
# self.setGeometry(780, 420, 240, 200)
|
||||
|
||||
# 위젯의 크기를 설정합니다.
|
||||
self.resize(800, 800)
|
||||
self.resize(400, 600)
|
||||
self.center() # 화면 가운데에 위치시키는 메서드
|
||||
|
||||
# 레이아웃 설정
|
||||
|
|
@ -125,6 +125,10 @@ class LoginWidget(QtWidgets.QWidget):
|
|||
self.loginButton = QtWidgets.QPushButton("로그인")
|
||||
self.loginButton.clicked.connect(self.login)
|
||||
|
||||
self.DeleteDB_by_ID = QtWidgets.QLineEdit()
|
||||
self.DeleteDB_by_ID.setPlaceholderText("상품ID 입력시 해당 DB 삭제")
|
||||
|
||||
|
||||
self.alwaysOnTopSwitch = ToggleSwitch(self)
|
||||
self.alwaysOnTopSwitch.move(10, 10)
|
||||
self.alwaysOnTopSwitch.setChecked(True) # 기본 상태는 ON
|
||||
|
|
@ -220,13 +224,13 @@ class LoginWidget(QtWidgets.QWidget):
|
|||
# label = QtWidgets.QLabel(label_text)
|
||||
# setting_layout.addWidget(label) # 설명 라벨은 2열(인덱스 1)에 배치
|
||||
|
||||
setting_layout.addLayout(self.modifyProductNameSwitch_layout)
|
||||
setting_layout.addLayout(self.modifyProductOptionsSwitch_layout)
|
||||
setting_layout.addLayout(self.modifyProductPriceSwitch_layout)
|
||||
setting_layout.addLayout(self.modifyProductTagSwitch_layout)
|
||||
setting_layout.addLayout(self.modifyProductThumbSwitch_layout)
|
||||
setting_layout.addLayout(self.modifyProductOptionsSwitch_layout)
|
||||
setting_layout.addLayout(self.modifyProductDetailSwitch_layout)
|
||||
setting_layout.addLayout(self.modifyImageTranslationSwitch_layout)
|
||||
setting_layout.addLayout(self.modifyProductPriceSwitch_layout)
|
||||
setting_layout.addLayout(self.modifyProductNameSwitch_layout)
|
||||
setting_layout.addLayout(self.uploadToMarketSwitch_layout)
|
||||
setting_layout.addLayout(self.alwaysOnTopSwitch_layout)
|
||||
setting_layout.addLayout(self.shutdownSwitch_layout)
|
||||
|
|
|
|||
7
main.py
7
main.py
|
|
@ -5,7 +5,7 @@ from selenium.webdriver.chrome.options import Options
|
|||
from selenium.webdriver.chrome.service import Service #웨일
|
||||
# from webdriver_manager.chrome import ChromeDriverManager #웨일
|
||||
import time
|
||||
import datetime
|
||||
from datetime import datetime
|
||||
import ctypes
|
||||
import atexit
|
||||
import platform, os, sys
|
||||
|
|
@ -115,6 +115,9 @@ def DB_setting():
|
|||
"login_result": login_widget.result,
|
||||
}
|
||||
|
||||
tag = login_info["whether_modifyImageTanslation"]
|
||||
|
||||
logger.debug(f"whether_modifyImageTanslation : {tag}")
|
||||
return login_info, result, mongo_config
|
||||
|
||||
def main():
|
||||
|
|
@ -127,7 +130,7 @@ def main():
|
|||
atexit.register(restore_sleep_mode)
|
||||
atexit.register(record_logout_time)
|
||||
|
||||
if login_info.whether_shutdownAfterComplete:
|
||||
if login_info["whether_shutdownAfterComplete"]:
|
||||
atexit.register(shutdown_system)
|
||||
|
||||
atexit.register(send_exit_message)
|
||||
|
|
|
|||
|
|
@ -182,6 +182,18 @@ def modify_products(driver, gemini, mongo_config, login_info, set_num_modify):
|
|||
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"],
|
||||
}
|
||||
|
||||
steps_and_actions = [
|
||||
('tag_modification', edit_tag, (driver, product_infos[i-1])),
|
||||
('thumbnail_modification', modify_thumb_page, (driver, product_infos[i-1])),
|
||||
|
|
@ -189,21 +201,10 @@ def modify_products(driver, gemini, mongo_config, login_info, set_num_modify):
|
|||
('detail_page_modification', modify_detail_page, (driver, product_infos[i-1], gemini, 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])),
|
||||
('trans_detailImage', tran_detail_image, (driver, product_infos[i-1])),
|
||||
# ('trans_detailImage', tran_detail_image, (driver, product_infos[i-1])),
|
||||
('upload_to_market', upload_to_market, (driver, product_infos[i-1])),
|
||||
]
|
||||
|
||||
# 상품 수정 관련 작업을 조건에 따라 수행하는 로직
|
||||
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,
|
||||
}
|
||||
|
||||
# 상품ID 복사
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -56,11 +56,11 @@ def parse_naver_shopping(keyword, naver_code, isOverseas=1, sortcount=10):
|
|||
logger.debug("NEXT_DATA JSON을 파싱하는 중 오류 발생:", e)
|
||||
# 예외 처리 코드 추가
|
||||
|
||||
# "__NEXT_DATA__" 파싱하기
|
||||
logger.debug("NEXT_DATA 파싱 완료")
|
||||
# # "__NEXT_DATA__" 파싱하기
|
||||
# logger.debug("NEXT_DATA 파싱 완료")
|
||||
|
||||
next_data_str = soup.find("script", {"id": "__NEXT_DATA__"}).string
|
||||
next_data_json = json.loads(next_data_str)
|
||||
# next_data_str = soup.find("script", {"id": "__NEXT_DATA__"}).string
|
||||
# next_data_json = json.loads(next_data_str)
|
||||
|
||||
# products 리스트 가져오기
|
||||
logger.debug("products 리스트 가져오기")
|
||||
|
|
|
|||
Loading…
Reference in New Issue