일부수정

This commit is contained in:
R5600U_PC 2024-10-18 16:59:41 +09:00
parent 53336be4aa
commit 447fea052c
9 changed files with 162 additions and 86 deletions

View File

@ -267,31 +267,19 @@ class BrowserController:
def fetch_image_urls(self, html_content):
"""
HTML 콘텐츠에서 모든 <img> 태그의 URL을 순서대로 추출하고 중복 제거.
HTML 콘텐츠에서 모든 <img> 태그의 URL을 HTML 소스의 순서대로 추출.
"""
soup = BeautifulSoup(html_content, 'html.parser')
# 중복된 이미지를 제거하기 위해 set 사용
image_urls_set = set()
# class="image_resized"를 가진 모든 <img> 태그 찾기
images_resized = soup.find_all('img', class_='image_resized')
for img in images_resized:
# HTML 소스에서 순서대로 <img> 태그를 찾음
image_urls = []
img_tags = soup.find_all('img')
for img in img_tags:
if img and 'src' in img.attrs:
image_urls_set.add(img['src']) # 중복을 방지하기 위해 set에 추가
image_urls.append(img['src']) # URL을 리스트에 추가
# <figure class="image"> 내부의 모든 <img> 태그 찾기
figures = soup.find_all('figure', class_='image')
for figure in figures:
img_tag = figure.find('img')
if img_tag and 'src' in img_tag.attrs:
image_urls_set.add(img_tag['src']) # 중복을 방지하기 위해 set에 추가
# set을 list로 변환하여 반환 (순서 유지가 필요하면 set 대신 리스트로 처리해야 함)
image_urls = list(image_urls_set)
return image_urls
async def close_ad_if_exists(self):
"""광고 다이얼로그가 있으면 닫기 버튼을 클릭하는 메서드"""
@ -521,7 +509,8 @@ class BrowserController:
# 나머지 옵션들은 - 없이 숫자 접두사로 표시
for i, option in enumerate(option_data[1:], start=2):
option_text = option[0] if isinstance(option, tuple) else option
option_prefix = f"{i}. "
# option_prefix = f"{i}. "
option_prefix = ""
await input_field.type(option_prefix + option_text)
await input_field.press('Enter') # 엔터 키를 입력하여 줄바꿈
@ -545,12 +534,12 @@ class BrowserController:
self.logger.debug(f"이미지 URL 추출 중 오류: {e}", exc_info=True)
return []
async def paste_image_in_chrome(self, clipboardImageManager, url):
def paste_image_in_chrome(self, clipboardImageManager, url):
"""크롬으로 포커스를 옮기고 클립보드의 이미지를 붙여넣고 엔터 입력"""
self.logger.debug("크롬으로 포커스를 옮기고 클립보드의 이미지를 붙여넣고 엔터 입력")
try:
self.switch_to_chrome() # 크롬으로 포커스 이동
await clipboardImageManager.process_clipboard(url) # 클립보드 내용을 처리
clipboardImageManager.process_clipboard(url) # 클립보드 내용을 처리
# clipboard_content = pyperclip.paste()
if clipboardImageManager.is_clipboard_image():
pyautogui.hotkey('ctrl', 'v') # 클립보드 이미지 붙여넣기

View File

@ -179,16 +179,19 @@ class ClipboardImageManager:
else:
self.logger.debug(f"이미지 로딩 실패, HTTP 상태 코드: {response.status_code}. 재시도 {retries + 1}/{max_retries}")
retries += 1
await asyncio.sleep(random.randint(2, 5)) # 2~5초 대기 후 재시도
# await asyncio.sleep(random.randint(2, 5)) # 2~5초 대기 후 재시도
time.sleep(random.randint(2, 5))
except Exception as e:
self.logger.debug(f"이미지 로딩 중 오류 발생: {e}. 재시도 {retries + 1}/{max_retries}")
retries += 1
await asyncio.sleep(random.randint(2, 5)) # 예외 발생 시 대기 후 재시도
# await asyncio.sleep(random.randint(2, 5)) # 예외 발생 시 대기 후 재시도
time.sleep(random.randint(2, 5))
self.logger.debug("이미지 다운로드 최대 재시도 횟수를 초과했습니다.")
return None
async def process_clipboard(self, original_url, path=None):
def process_clipboard(self, original_url, path=None):
"""클립보드의 내용을 처리하고, 필요한 경우 이미지 변환, 크롭 또는 클립보드 비우기"""
try:
@ -248,7 +251,7 @@ class ClipboardImageManager:
self.logger.info("[process_clipboard] 클립보드에 이미지 없음")
if original_url:
image = await self.download_image_from_url(original_url)
image = self.download_image_from_url(original_url)
if image:
self.logger.debug("원본 이미지 다운로드 성공, 클립보드에 저장 중...")
self.set_image_to_clipboard(image) # 크롭 없이 저장

2
gui.py
View File

@ -846,7 +846,7 @@ class TranslationApp(QWidget):
self.whale_translator.translate_image(url)
self.logger.debug(f"paste_image_in_chrome - 이미지 붙여넣기")
await self.browser_controller.paste_image_in_chrome(self.clipboardImageManager, url)
self.browser_controller.paste_image_in_chrome(self.clipboardImageManager, url)
self.logger.debug(f"Progress Update")
self.update_detail_progress(i,total_images)

View File

@ -27,6 +27,7 @@ class PriceHandler:
self.first_delv_fee = 0
self.exchange_fee = 0
self.sold_price = 0
self.initail_margin_cost = 0
# Locator들을 미리 로드하여 초기화 - locator_template:동적 로딩, locator:고정로딩
self.product_cost_locator_template = self.locator_manager.get_locator('PriceLocators', 'product_cost_locator')
@ -331,6 +332,10 @@ class PriceHandler:
- adjusted_margin (int): 적정 판매가에 맞게 조정된 더하기 마진.
"""
try:
if not option_data or len(option_data) == 0:
self.logger.warning(f"옵션 데이터가 없거나 비어있습니다. 계산원가 기준 더하기마진({self.initail_margin_cost}) 을 반환합니다.")
return self.initail_margin_cost # 기본 더하기 마진 설정
total_option_price = math.ceil(sum([option['price'] for option in option_data]) / len(option_data))
self.logger.debug(f"총 옵션 기준 판매가 평균: {total_option_price}")
@ -503,11 +508,11 @@ class PriceHandler:
initail_shipping_cost = self.round_to_UP(initail_shipping_cost)
self.logger.debug(f"계산원가 기준 해외배송비: {initail_shipping_cost}")
initail_margin_cost = self.calculate_shipping_cost_with_extended_thresholds(10000, initial_cost_price)
initail_margin_cost = self.round_to_UP(initail_margin_cost)
self.logger.debug(f"계산원가 기준 더하기마진: {initail_margin_cost}")
self.initail_margin_cost = self.calculate_shipping_cost_with_extended_thresholds(10000, initial_cost_price)
self.initail_margin_cost = self.round_to_UP(self.initail_margin_cost)
self.logger.debug(f"계산원가 기준 더하기마진: {self.initail_margin_cost}")
result = int(initial_cost_price + initail_shipping_cost + initail_margin_cost)
result = int(initial_cost_price + initail_shipping_cost + self.initail_margin_cost)
self.logger.debug(f"원가기반 가격: {result}")
return result

BIN
src/img/translating.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -3,12 +3,17 @@ import time
import os
import subprocess
import win32gui, win32con, win32process
import pyscreeze
class WhaleTranslator:
def __init__(self, logger, error_image_filenames=['fail_translated1.png', 'fail_translated2.png'], pixel_check_interval=0.1, secret_mode=True, timeout=20, color_tolerance=20):
self.logger = logger
self.error_image_paths = [os.path.join(os.path.dirname(__file__), filename) for filename in error_image_filenames]
self.page_loading_icon_path = os.path.join(os.path.dirname(__file__), 'page_loading.png')
self.translating_image_path = os.path.join(os.path.dirname(__file__), 'translating.png')
# self.translating_image_path = os.path.join(os.path.dirname(__file__), 'src', 'img', 'translating.png')
self.pixel_check_interval = pixel_check_interval
self.whale_window_name = "새 시크릿 탭 - Whale" if secret_mode else "새 탭 - Whale"
@ -16,6 +21,7 @@ class WhaleTranslator:
self.timeout = timeout # 번역 성공 여부를 판단하기 위한 시간 제한 설정
self.color_tolerance = color_tolerance # 색상 허용 오차
self.colors = {'before': None, 'during': None, 'after': None} # 색상 기록
self.whale_rect = None
def start_whale_browser(self, url):
whale_path = r"C:\\Program Files\\Naver\\Naver Whale\\Application\\whale.exe"
@ -26,8 +32,9 @@ class WhaleTranslator:
hwnd = self.find_whale_window()
if hwnd:
self.set_window_position(hwnd, 100, 100, 1280, 720) # 위치 (100, 100), 크기 (1280x720)
self.set_window_position(hwnd, 1, 1, 1280, 720) # 위치 (100, 100), 크기 (1280x720)
self.update_whale_rect()
if hwnd:
win32gui.SetForegroundWindow(hwnd)
pyautogui.hotkey('ctrl', 'l')
@ -36,10 +43,10 @@ class WhaleTranslator:
pyautogui.press('enter')
time.sleep(2)
# 페이지 로딩 완료 대기
self.wait_for_loading_icon_to_disappear()
# # 페이지 로딩 완료 대기
# self.wait_for_loading_icon_to_disappear()
self.move_mouse_to_center(hwnd) # 마우스 중앙으로 이동
self.move_mouse_to_center() # 마우스 중앙으로 이동
time.sleep(0.5) # 마우스 이동 후 대기
original_color = self.get_mouse_position_color() # 현재 색상 가져오기
@ -50,21 +57,24 @@ class WhaleTranslator:
pyautogui.rightClick()
time.sleep(1)
pyautogui.press('r')
time.sleep(1)
# 번역 성공 여부 확인
result = self.check_translation_by_color_change(original_color)
if result == "success":
print("번역이 성공적으로 완료되었습니다!")
elif result == "error":
print("번역에 실패했습니다.")
else:
print("번역 상태를 확인하지 못했습니다.")
# 번역 시작 감지 (translating.png 확인)
if self.is_translating():
self.logger.debug("번역 시작 감지 ('translating.png' 발견)")
# 번역 중 색상 지속 확인
result = self.check_translation_by_color_change()
if result == "success":
self.logger.debug(f"URL: {url} - 번역이 성공적으로 완료되었습니다!")
elif result == "error":
self.logger.debug(f"URL: {url} - 번역에 실패했습니다.")
else:
self.logger.debug(f"URL: {url} - 번역 상태를 확인하지 못했습니다.")
# 최종 색상 출력
self.logger.debug(f"번역 전 색상: {self.colors['before']}")
self.logger.debug(f"번역 중 색상: {self.colors['during']}")
self.logger.debug(f"번역 후 색상: {self.colors['after']}")
self.logger.debug(f"URL: {url} - 번역 중 색상: {self.colors['during']}")
self.logger.debug(f"URL: {url} - 번역 후 색상: {self.colors['after']}")
def wait_for_loading_icon_to_disappear(self, max_wait=20):
"""
@ -75,19 +85,65 @@ class WhaleTranslator:
while time.time() - start_time < max_wait:
try:
# 화면에서 아이콘 위치 확인 시도
icon_location = pyautogui.locateOnScreen(self.page_loading_icon_path, confidence=0.9)
icon_location = self.find_image_with_confidence(self.page_loading_icon_path, confidence=0.9)
if icon_location:
print("페이지 로딩 중...")
self.logger.debug("페이지 로딩 중...")
time.sleep(0.5) # 간격을 두고 다시 확인
except pyautogui.ImageNotFoundException:
except pyautogui.ImageNotFoundException as e:
# 아이콘이 화면에 없으면 로딩 완료로 간주
print("페이지 로딩이 완료되었습니다.")
# self.logger.debug(f"페이지 로딩이 완료되었습니다. {e}", exc_info=True)
self.logger.debug(f"페이지 로딩이 완료되었습니다.")
return True # 로딩 완료
print("로딩 완료 대기 시간이 초과되었습니다.")
self.logger.debug("로딩 완료 대기 시간이 초과되었습니다.")
return False
def find_image_with_confidence(self, image_path, confidence=0.8):
"""이미지를 찾을 때 highest confidence 값을 로그로 출력하는 메서드"""
if self.whale_rect:
# 웨일 창의 크기를 region으로 설정
region = (self.whale_rect[0], self.whale_rect[1],
self.whale_rect[2] - self.whale_rect[0],
self.whale_rect[3] - self.whale_rect[1])
self.logger.debug(f"이미지를 찾을 영역: {region}")
try:
# locateOnScreen 시도
result = pyscreeze.locateOnScreen(image_path, confidence=confidence, region=region)
if result:
self.logger.debug(f"이미지를 찾았습니다: {image_path}")
return result
else:
raise pyscreeze.ImageNotFoundException("Could not locate the image")
except pyscreeze.ImageNotFoundException as e:
# 'Could not locate the image' 메시지 발생 시 최고 confidence 값 로그 출력
if hasattr(e, 'args') and 'highest confidence' in str(e.args[0]):
self.logger.error(f"이미지를 찾지 못했습니다만 유사한 부분을 찾았습니다. : [{e.args[0]}]")
else:
self.logger.error("이미지를 찾지 못했습니다. 이미지와 유사한 부분을 찾지 못했습니다.")
return None
def is_translating(self):
"""translating.png가 화면에 나타나는지 확인하여 번역 시작 여부를 판단"""
try:
# translating_location = pyautogui.locateOnScreen(self.translating_image_path, confidence=0.8)
translating_location = self.find_image_with_confidence(self.translating_image_path, confidence=0.8)
if translating_location:
return True
except pyautogui.ImageNotFoundException as e:
self.logger.debug(f"'translating.png'를 찾지 못했습니다. : {e}", exc_info=True)
return False
def update_whale_rect(self):
"""웨일 창의 위치 및 크기 rect를 업데이트"""
if self.whale_hwnd:
self.whale_rect = win32gui.GetWindowRect(self.whale_hwnd)
self.logger.debug(f"웨일 창 크기 및 위치 저장: {self.whale_rect}")
def find_whale_window(self):
"""웨일 창을 제목을 기준으로 찾는 메서드"""
def callback(hwnd, extra):
@ -99,8 +155,10 @@ class WhaleTranslator:
hwnd_list = []
win32gui.EnumWindows(callback, hwnd_list)
if hwnd_list:
self.logger.debug(f"웨일 창을 찾았습니다: {hwnd_list[0]}")
return hwnd_list[0]
self.whale_hwnd = hwnd_list[0]
self.logger.debug(f"웨일 창을 찾았습니다: {self.whale_hwnd}")
self.update_whale_rect()
return self.whale_hwnd
else:
self.logger.debug("웨일 창을 찾지 못했습니다.")
return None
@ -111,40 +169,60 @@ class WhaleTranslator:
win32gui.SetWindowPos(hwnd, None, x, y, width, height, win32con.SWP_NOZORDER | win32con.SWP_NOACTIVATE)
self.logger.debug(f"창 위치 및 크기 설정: 위치({x}, {y}), 크기({width}x{height})")
def move_mouse_to_center(self, hwnd):
def move_mouse_to_center(self):
"""웨일 브라우저 창의 중앙으로 마우스 커서를 이동"""
rect = win32gui.GetWindowRect(hwnd) # 창 위치 및 크기 가져오기
center_x = (rect[0] + rect[2]) // 2 # 가로 중앙 계산
center_y = (rect[1] + rect[3]) // 2 # 세로 중앙 계산
pyautogui.moveTo(center_x, center_y)
self.logger.debug(f"마우스 커서를 창 중앙으로 이동: ({center_x}, {center_y})")
if self.whale_rect:
center_x = (self.whale_rect[0] + self.whale_rect[2]) // 2 # 가로 중앙 계산
center_y = (self.whale_rect[1] + self.whale_rect[3]) // 2 # 세로 중앙 계산
pyautogui.moveTo(center_x, center_y)
self.logger.debug(f"마우스 커서를 창 중앙으로 이동: ({center_x}, {center_y})")
else:
self.logger.error("웨일 창의 크기를 알 수 없습니다. 먼저 창을 찾으세요.")
def check_translation_by_color_change(self, original_color):
def is_color_changed(self, current_color):
"""현재 색상이 변화했는지 확인 (필터 제거 여부)"""
if self.colors['during'] is None:
self.colors['during'] = current_color
return not self.is_similar_color(current_color, self.colors['during'])
def check_translation_by_color_change(self):
start_time = time.time()
# 번역 시작 감지 (translating.png 확인)
while time.time() - start_time < self.timeout:
current_color = self.get_mouse_position_color()
if self.colors['during'] is None: # 번역 중 첫 색상 기록
self.colors['during'] = current_color
self.logger.debug(f"현재 색상: {current_color}")
if self.is_similar_color(current_color, original_color):
# 필터가 사라져서 색상이 변했는지 확인
if self.is_color_changed(current_color):
self.colors['after'] = current_color
self.logger.debug("번역 성공 감지 (원래 색상과 유사)")
return "success"
if not self.is_similar_color(current_color, original_color):
self.logger.debug("번역 중 상태 감지 (필터 색상)")
self.logger.debug("색상 변화 감지 (필터 제거됨)")
# translating.png가 여전히 존재하는지 확인하여 번역 성공 여부 판단
result = self.find_image_with_confidence(self.translating_image_path, confidence=0.8)
if result:
return "success"
else:
return "error"
else:
# 번역 실패 이미지 확인
if self.is_translation_failed():
self.colors['after'] = current_color
return "error"
time.sleep(self.pixel_check_interval)
# 타임아웃 발생 시, 번역 성공으로 간주
# 타임아웃 발생 시 translating.png 여부로 번역 성공/실패 판단
self.colors['after'] = current_color
self.logger.debug("번역 성공으로 간주 (타임아웃)")
return "success"
result = self.find_image_with_confidence(self.translating_image_path, confidence=0.8)
if result:
self.logger.debug("번역 성공으로 간주 (타임아웃 후 translating.png 존재)")
return "success"
else:
self.logger.debug("번역 실패로 간주 (타임아웃 후 translating.png 없음)")
return "error"
def is_similar_color(self, color1, color2):
"""색상이 유사한지 확인 (허용 오차 적용)"""
@ -160,12 +238,10 @@ class WhaleTranslator:
def is_translation_failed(self):
"""번역 실패 이미지 확인"""
for image_path in self.error_image_paths:
try:
if pyautogui.locateOnScreen(image_path, confidence=0.8):
self.logger.error(f"번역 실패: '{os.path.basename(image_path)}' 메시지가 감지되었습니다.")
return True
except pyautogui.ImageNotFoundException:
self.logger.debug(f"번역 실패 이미지가 화면에 없습니다: {os.path.basename(image_path)}")
result = self.find_image_with_confidence(image_path, confidence=0.8)
if result:
self.logger.error(f"번역 실패: '{os.path.basename(image_path)}' 메시지가 감지되었습니다.")
return True
return False
# 예제 사용법
@ -179,4 +255,7 @@ logger = logging.getLogger(__name__)
translator = WhaleTranslator(logger, ['fail_translated1.png', 'fail_translated2.png'])
# 브라우저 실행 후 URL로 이동 및 번역 성공 여부 확인
translator.start_whale_browser("https://file.percenty.co.kr/public/652bed8e865b1f32ea62bf1f/products/66ff967773994c46d388bb36/82d07178-ae60-49f7-a489-e02801ff7b06.jpg")
# translator.start_whale_browser("https://file.percenty.co.kr/public/652bed8e865b1f32ea62bf1f/products/66ff967773994c46d388bb36/82d07178-ae60-49f7-a489-e02801ff7b06.jpg")
translator.start_whale_browser('https://img.alicdn.com/imgextra/i4/735691568/O1CN01sRUYqb1NSBuefMBlw_!!735691568.jpg_Q75.jpg')
translator.start_whale_browser('https://img.alicdn.com/imgextra/i4/1773313923/O1CN01VMRs1Z1eqmfYSXQDu_!!1773313923.jpg_Q75.jpg')

BIN
test/page_loading_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

BIN
test/translating.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -32,7 +32,7 @@ class WhaleTranslator:
self.page_loading_icon_path = os.path.join(img_dir, 'page_loading.png')
self.translation_success_flag = False # 번역 성공 플래그
# self.failure_count = 0 # 실패 횟수
self.failure_count = 0 # 실패 횟수
if isSecret:
self.whale_window_name = "새 시크릿 탭 - Whale"
@ -89,10 +89,10 @@ class WhaleTranslator:
if self.vd_mode:
self.return_to_virtual_desktop_1() # 가상 데스크탑 1로 복귀
# def reset_failures(self):
# """실패 횟수를 초기화"""
# self.failure_count = 0
# self.logger.debug("실패 횟수가 초기화되었습니다.")
def reset_failures(self):
"""실패 횟수를 초기화"""
self.failure_count = 0
self.logger.debug("실패 횟수가 초기화되었습니다.")
# def handle_translation_failure(self):
# """번역 실패 시 처리"""