Merge branch 'pyside6' of https://git.cckb9998.synology.me/ckh08045/autoTrans into pyside6
This commit is contained in:
commit
984c718300
|
|
@ -2,5 +2,6 @@ Include/
|
|||
Lib/
|
||||
Scripts/
|
||||
__pycache__/
|
||||
__pycache__/
|
||||
pyvenv.cfg
|
||||
*.log
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
43655
appTranslator.log
43655
appTranslator.log
File diff suppressed because one or more lines are too long
|
|
@ -54,7 +54,7 @@ class BrowserController:
|
|||
else:
|
||||
self.logger.debug(f'크롬 창 핸들: {self.chrome_hwnd}')
|
||||
|
||||
await self.page.wait_for_load_state('networkidle')
|
||||
# await self.page.wait_for_load_state('networkidle', timeout=10000)
|
||||
|
||||
async def login(self, admin_id, user_id, admin_password, user_password, is_admin=False):
|
||||
"""로그인 처리"""
|
||||
|
|
@ -78,7 +78,7 @@ class BrowserController:
|
|||
|
||||
self.logger.debug(f'로그인 완료: {"관리자" if is_admin else "직원"} 계정')
|
||||
|
||||
await self.page.wait_for_load_state('networkidle')
|
||||
# await self.page.wait_for_load_state('networkidle', timeout=10000)
|
||||
|
||||
await self.close_ad_if_exists()
|
||||
|
||||
|
|
@ -159,59 +159,6 @@ class BrowserController:
|
|||
self.logger.error(f"상품명 수집 중 오류: {e}")
|
||||
return "수집 오류 발생"
|
||||
|
||||
|
||||
async def extract_image_urls(self):
|
||||
"""HTML에서 이미지 URL 추출 및 img 태그 삭제 후 소스 버튼 다시 클릭"""
|
||||
self.logger.debug('이미지 URL을 추출 중...')
|
||||
|
||||
# 소스 버튼 클릭
|
||||
await self.page.click("button[data-cke-tooltip-text='소스']")
|
||||
self.logger.debug('소스 버튼 클릭 완료.')
|
||||
|
||||
# 'data-value' 속성 값을 추출 (textarea 요소)
|
||||
textarea = await self.page.wait_for_selector('div.ck-source-editing-area')
|
||||
data_value = await textarea.get_attribute("data-value")
|
||||
|
||||
if data_value:
|
||||
self.logger.debug('data-value 속성에서 HTML 수집 완료.')
|
||||
|
||||
# 이미지 URL 추출
|
||||
image_urls = self.fetch_image_urls(data_value)
|
||||
self.logger.debug(f'추출된 이미지 URL 수: {len(image_urls)}')
|
||||
# 추출된 URL 반환
|
||||
self.logger.debug('img 태그를 삭제 중...')
|
||||
|
||||
await self.page.wait_for_load_state('domcontentloaded') # 페이지 로딩 완료 대기
|
||||
|
||||
# data-value 속성을 가진 요소 선택
|
||||
data_value_element = await self.page.query_selector('div.ck-source-editing-area')
|
||||
|
||||
new_value = ""
|
||||
|
||||
if data_value_element:
|
||||
# 속성 변경 (원하는 텍스트로 변경하거나 ""으로 변경)
|
||||
# self.page.evaluate('(element, value) => element.setAttribute("data-value", value)', data_value_element, new_value)
|
||||
await self.page.evaluate(f'() => document.querySelector("div.ck-source-editing-area").setAttribute("data-value", "{new_value}")')
|
||||
|
||||
# 데이터가 제대로 변경되었는지 확인
|
||||
updated_value = await data_value_element.get_attribute('data-value')
|
||||
self.logger(f'Updated data-value: {updated_value}')
|
||||
|
||||
else:
|
||||
self.logger('Element with data-value not found.')
|
||||
|
||||
|
||||
self.logger.debug('img 태그 삭제 완료.')
|
||||
|
||||
# 소스 버튼 다시 클릭
|
||||
await self.page.click("button[data-cke-tooltip-text='소스']")
|
||||
self.logger.debug('소스 버튼 재 클릭 완료.')
|
||||
|
||||
return image_urls
|
||||
else:
|
||||
self.logger.debug('data-value 속성에 데이터가 없습니다.')
|
||||
return []
|
||||
|
||||
def fetch_image_urls(self, html_content):
|
||||
"""
|
||||
HTML 콘텐츠에서 모든 <img> 태그의 URL을 순서대로 추출
|
||||
|
|
@ -270,10 +217,11 @@ class BrowserController:
|
|||
"""현재 페이지의 세부사항 수정 및 업로드 버튼을 찾기"""
|
||||
try:
|
||||
# 페이지 로딩을 기다림
|
||||
await self.page.wait_for_load_state('networkidle') # 네트워크 요청이 모두 끝날 때까지 대기
|
||||
# await self.page.wait_for_load_state('networkidle', timeout=10000) # 네트워크 요청이 끝난 후 대기
|
||||
|
||||
# 페이지 끝까지 스크롤하여 모든 동적 요소 로드
|
||||
await self.scroll_page_to_bottom()
|
||||
await self.scroll_page_to_top()
|
||||
|
||||
|
||||
# # 스크롤하여 모든 버튼을 화면에 표시 (가장 하단까지 스크롤)
|
||||
|
|
@ -321,7 +269,59 @@ class BrowserController:
|
|||
except Exception as e:
|
||||
self.logger.debug(f"옵션 탭 클릭 중 오류: {e}", exc_info=True)
|
||||
|
||||
async def extract_image_urls(self):
|
||||
# async def extract_image_urls(self):
|
||||
# """HTML에서 이미지 URL 추출 및 img 태그 삭제 후 소스 버튼 다시 클릭"""
|
||||
# self.logger.debug('이미지 URL을 추출 중...')
|
||||
|
||||
# # 소스 버튼 클릭
|
||||
# await self.page.click("button[data-cke-tooltip-text='소스']")
|
||||
# self.logger.debug('소스 버튼 클릭 완료.')
|
||||
|
||||
# # 'data-value' 속성 값을 추출 (textarea 요소)
|
||||
# textarea = await self.page.wait_for_selector('div.ck-source-editing-area')
|
||||
# data_value = await textarea.get_attribute("data-value")
|
||||
|
||||
# if data_value:
|
||||
# self.logger.debug('data-value 속성에서 HTML 수집 완료.')
|
||||
|
||||
# # 이미지 URL 추출
|
||||
# image_urls = self.fetch_image_urls(data_value)
|
||||
# self.logger.debug(f'추출된 이미지 URL 수: {len(image_urls)}')
|
||||
# # 추출된 URL 반환
|
||||
# self.logger.debug('img 태그를 삭제 중...')
|
||||
|
||||
# await self.page.wait_for_load_state('domcontentloaded') # 페이지 로딩 완료 대기
|
||||
|
||||
# # data-value 속성을 가진 요소 선택
|
||||
# data_value_element = await self.page.query_selector('div.ck-source-editing-area')
|
||||
|
||||
# new_value = ""
|
||||
|
||||
# if data_value_element:
|
||||
# # 속성 변경 (원하는 텍스트로 변경하거나 ""으로 변경)
|
||||
# # self.page.evaluate('(element, value) => element.setAttribute("data-value", value)', data_value_element, new_value)
|
||||
# await self.page.evaluate(f'() => document.querySelector("div.ck-source-editing-area").setAttribute("data-value", "{new_value}")')
|
||||
|
||||
# # 데이터가 제대로 변경되었는지 확인
|
||||
# updated_value = await data_value_element.get_attribute('data-value')
|
||||
# self.logger(f'Updated data-value: {updated_value}')
|
||||
|
||||
# else:
|
||||
# self.logger('Element with data-value not found.')
|
||||
|
||||
|
||||
# self.logger.debug('img 태그 삭제 완료.')
|
||||
|
||||
# # 소스 버튼 다시 클릭
|
||||
# await self.page.click("button[data-cke-tooltip-text='소스']")
|
||||
# self.logger.debug('소스 버튼 재 클릭 완료.')
|
||||
|
||||
# return image_urls
|
||||
# else:
|
||||
# self.logger.debug('data-value 속성에 데이터가 없습니다.')
|
||||
# return []
|
||||
|
||||
async def extract_image_urls(self, optionHandler, is_option_data=False):
|
||||
"""상세페이지에서 이미지 URL 추출"""
|
||||
try:
|
||||
# 소스 편집 모드로 전환
|
||||
|
|
@ -338,7 +338,7 @@ class BrowserController:
|
|||
|
||||
# HTML 소스에서 이미지 URL 삭제
|
||||
self.logger.debug('img 태그를 삭제 중...')
|
||||
await self.page.wait_for_load_state('domcontentloaded') # 페이지 로딩 완료 대기
|
||||
# await self.page.wait_for_load_state('domcontentloaded') # 페이지 로딩 완료 대기
|
||||
# data-value 속성을 가진 요소 선택
|
||||
data_value_element = await self.page.query_selector('div.ck-source-editing-area')
|
||||
new_value = ""
|
||||
|
|
@ -358,6 +358,51 @@ class BrowserController:
|
|||
await self.page.click('button[data-cke-tooltip-text="소스"]')
|
||||
self.logger.debug('소스 버튼 재 클릭 완료.')
|
||||
|
||||
if is_option_data:
|
||||
self.logger.debug('옵션 데이터 입력 시작')
|
||||
option_data = optionHandler.get_selected_translated_options()
|
||||
|
||||
# 옵션 입력 필드 선택
|
||||
input_field_selector = '//*[@id="productMainContentContainerId"]/div/div/div[2]/div[2]/div[2]/div' # 여기에 적절한 입력 필드의 셀렉터를 입력
|
||||
input_field = await self.page.wait_for_selector(input_field_selector)
|
||||
|
||||
|
||||
# 선두부 텍스트 입력
|
||||
await input_field.type('---')
|
||||
await input_field.press('Enter')
|
||||
pyautogui.typewrite('## > 안녕하세요 혜리수샵입니다.')
|
||||
await input_field.press('Enter')
|
||||
|
||||
await input_field.type('# 마켓정책으로 인해 모든 옵션이 노출되지 않을수도 있습니다.')
|
||||
await input_field.type('**반드시 옵션사진과 옵션이름을 확인하시고 구매하시기 바랍니다.**')
|
||||
await input_field.press('Enter')
|
||||
await input_field.type('---')
|
||||
await input_field.press('Enter')
|
||||
|
||||
# 각 옵션을 한 줄씩 입력
|
||||
for i, option in enumerate(option_data, start=1):
|
||||
if isinstance(option, tuple):
|
||||
option_text = option[0] # 튜플의 첫 번째 요소를 사용
|
||||
else:
|
||||
option_text = option
|
||||
|
||||
# 옵션을 A. B. 등으로 표시하며 입력
|
||||
option_prefix = f"{chr(64 + i)}. " # A, B, C...
|
||||
await input_field.type(option_prefix + option_text)
|
||||
await input_field.press('Enter') # 엔터 키를 입력하여 줄바꿈
|
||||
|
||||
# 후두부 텍스트 입력
|
||||
await input_field.type('---')
|
||||
await input_field.press('Enter')
|
||||
await input_field.type('나열된 옵션목록 이외의 옵션이 필요하실 경우 고객센터로 연락주세요.')
|
||||
await input_field.press('Enter')
|
||||
await input_field.type('---')
|
||||
await input_field.press('Enter')
|
||||
await input_field.press('Enter')
|
||||
await input_field.press('Enter')
|
||||
|
||||
self.logger.debug('옵션 데이터 입력 완료 후 엔터 입력')
|
||||
|
||||
return image_urls
|
||||
except Exception as e:
|
||||
self.logger.debug(f"이미지 URL 추출 중 오류: {e}", exc_info=True)
|
||||
|
|
@ -367,14 +412,14 @@ class BrowserController:
|
|||
"""크롬으로 포커스를 옮기고 클립보드의 이미지를 붙여넣고 엔터 입력"""
|
||||
try:
|
||||
self.switch_to_chrome() # 크롬으로 포커스 이동
|
||||
clipboardImageManager.process_clipboard(url) # 클립보드 내용을 처리
|
||||
await clipboardImageManager.process_clipboard(url) # 클립보드 내용을 처리
|
||||
# clipboard_content = pyperclip.paste()
|
||||
if clipboardImageManager.is_clipboard_image():
|
||||
pyautogui.hotkey('ctrl', 'v') # 클립보드 이미지 붙여넣기
|
||||
pyautogui.press('right') # 오른쪽 입력
|
||||
self.logger.debug("이미지 붙여넣기 완료.")
|
||||
self.logger.debug("이미지 붙여넣기 완료로 클립보드 비우기.")
|
||||
clipboardImageManager.clear_clipboard()
|
||||
await clipboardImageManager.clear_clipboard()
|
||||
else:
|
||||
self.logger.debug("클립보드가 비어있습니다.")
|
||||
except Exception as e:
|
||||
|
|
@ -416,7 +461,7 @@ class BrowserController:
|
|||
|
||||
if next_page_button:
|
||||
await next_page_button.click() # 페이지 버튼 클릭
|
||||
await self.page.wait_for_load_state('domcontentloaded') # 페이지 로딩이 완료될 때까지 대기
|
||||
# await self.page.wait_for_load_state('domcontentloaded') # 페이지 로딩이 완료될 때까지 대기
|
||||
self.logger.debug(f"페이지 {next_page_number}로 이동 완료.")
|
||||
return True
|
||||
else:
|
||||
|
|
@ -456,32 +501,77 @@ class BrowserController:
|
|||
- max_scrolls: 최대 스크롤 횟수.
|
||||
"""
|
||||
scroll_count = 0
|
||||
|
||||
self.logger.debug(f"스크롤 시작")
|
||||
|
||||
# 현재 페이지 높이 가져오기
|
||||
last_height = await self.page.evaluate("document.body.scrollHeight")
|
||||
self.logger.debug(f"현재 페이지 높이 가져오기 - {last_height}")
|
||||
|
||||
while scroll_count < max_scrolls:
|
||||
if direction == "down":
|
||||
# 아래로 스크롤
|
||||
await self.page.mouse.wheel(0, 1000)
|
||||
self.logger.debug(f"scroll_count[{scroll_count}]회 : 휠 아래로 1000px")
|
||||
await self.page.evaluate("window.scrollBy(0, 1000);")
|
||||
elif direction == "up":
|
||||
# 위로 스크롤
|
||||
await self.page.mouse.wheel(0, -1000)
|
||||
self.logger.debug(f"scroll_count[{scroll_count}]회 : 휠 위로 1000px")
|
||||
await self.page.evaluate("window.scrollBy(0, -1000);")
|
||||
else:
|
||||
raise ValueError("direction 인자는 'down' 또는 'up'만 허용됩니다.")
|
||||
|
||||
self.logger.debug(f"pause_time 슬립 : {pause_time}")
|
||||
await asyncio.sleep(pause_time)
|
||||
|
||||
# 새로운 페이지 높이 가져오기
|
||||
new_height = await self.page.evaluate("document.body.scrollHeight")
|
||||
self.logger.debug(f"새로운 페이지 높이 가져오기 - {new_height}")
|
||||
|
||||
# 스크롤이 더 이상 필요 없는 경우(페이지 끝에 도달)
|
||||
if direction == "down" and new_height == last_height:
|
||||
self.logger.debug(f"페이지 끝에 도달했습니다. 스크롤 횟수: {scroll_count}")
|
||||
break
|
||||
elif direction == "up" and new_height == 0:
|
||||
self.logger.debug(f"페이지 시작에 도달했습니다. 스크롤 횟수: {scroll_count}")
|
||||
break
|
||||
|
||||
self.logger.debug(f"새로운 페이지 높이를 현재높이로 재설정 - {new_height}")
|
||||
last_height = new_height
|
||||
scroll_count += 1
|
||||
self.logger.debug(f"스크롤 카운트 + 1")
|
||||
|
||||
if scroll_count == max_scrolls:
|
||||
self.logger.debug("최대 스크롤 횟수에 도달했습니다.")
|
||||
|
||||
async def scroll_with_keyboard(self, direction="down", pause_time=0.5, max_scrolls=50):
|
||||
"""
|
||||
키보드를 사용하여 페이지를 위나 아래로 천천히 스크롤.
|
||||
|
||||
Parameters:
|
||||
- direction: 스크롤 방향 ("down"은 아래로, "up"은 위로).
|
||||
- pause_time: 스크롤 사이의 대기 시간 (초).
|
||||
- max_scrolls: 최대 스크롤 횟수.
|
||||
"""
|
||||
scroll_count = 0
|
||||
|
||||
while scroll_count < max_scrolls:
|
||||
if direction == "down":
|
||||
# 아래로 스크롤 (Page Down 키 사용)
|
||||
await self.page.keyboard.press("PageDown")
|
||||
elif direction == "up":
|
||||
# 위로 스크롤 (Page Up 키 사용)
|
||||
await self.page.keyboard.press("PageUp")
|
||||
else:
|
||||
raise ValueError("direction 인자는 'down' 또는 'up'만 허용됩니다.")
|
||||
|
||||
await asyncio.sleep(pause_time)
|
||||
|
||||
# 스크롤 후 높이 확인
|
||||
new_height = await self.page.evaluate("document.body.scrollHeight")
|
||||
|
||||
# 아래로 스크롤 시, 페이지의 끝에 도달한 경우 종료
|
||||
if direction == "down" and new_height == last_height:
|
||||
break
|
||||
elif direction == "up" and new_height == 0:
|
||||
break # 위로 스크롤 시, 페이지의 시작에 도달하면 종료
|
||||
|
||||
last_height = new_height
|
||||
scroll_count += 1
|
||||
|
||||
if scroll_count == max_scrolls:
|
||||
self.logger.debug("최대 스크롤 횟수에 도달했습니다.")
|
||||
|
||||
|
||||
async def collect_product_info(self):
|
||||
"""
|
||||
상품 정보를 수집하는 메서드
|
||||
|
|
@ -537,7 +627,7 @@ class BrowserController:
|
|||
self.logger.debug(f'{index}번째 상품의 수정 버튼 클릭 중 오류: {str(e)}')
|
||||
|
||||
|
||||
async def scroll_page_to_bottom(self, pause_time=1):
|
||||
async def scroll_page_to_bottom(self, pause_time=0.2):
|
||||
"""페이지의 맨 아래까지 스크롤하여 모든 동적 요소를 로드"""
|
||||
self.logger.debug('페이지 스크롤 시작...')
|
||||
previous_height = await self.page.evaluate("() => document.body.scrollHeight")
|
||||
|
|
@ -551,4 +641,17 @@ class BrowserController:
|
|||
previous_height = current_height
|
||||
self.logger.debug('페이지 스크롤 완료.')
|
||||
|
||||
async def scroll_page_to_top(self, pause_time=0.2):
|
||||
"""페이지의 맨 위까지 스크롤"""
|
||||
self.logger.debug('페이지 위로 스크롤 시작...')
|
||||
previous_height = await self.page.evaluate("() => window.pageYOffset")
|
||||
|
||||
while previous_height > 0:
|
||||
await self.page.evaluate("window.scrollBy(0, -window.innerHeight);") # 한 화면씩 위로 스크롤
|
||||
await asyncio.sleep(pause_time) # 페이지 로딩 대기
|
||||
current_height = await self.page.evaluate("() => window.pageYOffset")
|
||||
if current_height == previous_height:
|
||||
break # 더 이상 스크롤할 내용이 없으면 종료
|
||||
previous_height = current_height
|
||||
|
||||
self.logger.debug('페이지 위로 스크롤 완료.')
|
||||
|
|
|
|||
14
gui.py
14
gui.py
|
|
@ -105,7 +105,7 @@ class TranslationApp(QWidget):
|
|||
|
||||
def initUI(self):
|
||||
self.setWindowFlags(Qt.WindowStaysOnTopHint)
|
||||
self.setGeometry(QRect(940, 500, 180, 400))
|
||||
self.setGeometry(QRect(1240, 750, 280, 600))
|
||||
self.setWindowTitle('이미지 번역 도구')
|
||||
|
||||
# 로그
|
||||
|
|
@ -375,7 +375,7 @@ class TranslationApp(QWidget):
|
|||
self.logger.debug(f'현재 페이지: {page_number}')
|
||||
|
||||
if not page_number == 1:
|
||||
self.browser_controller.scroll_with_wheel("up")
|
||||
self.browser_controller.scroll_page_to_top()
|
||||
self.logger.debug(f'1페이지가 아니므로 동적로딩을 위해 휠 스크롤 업')
|
||||
|
||||
# 4. 현재 페이지의 모든 "세부사항 수정 및 업로드" 버튼 찾기
|
||||
|
|
@ -532,21 +532,21 @@ class TranslationApp(QWidget):
|
|||
self.logger.debug('프로그램을 종료합니다...')
|
||||
self.save_settings()
|
||||
await self.browser_controller.close_browser() # 브라우저 종료
|
||||
await self.whale_translator.close_all_virtual_desktops()
|
||||
self.whale_translator.close_all_virtual_desktops()
|
||||
super().close()
|
||||
|
||||
async def detail_trans(self):
|
||||
# 상세페이지 탭 클릭
|
||||
await self.browser_controller.click_detail_tab()
|
||||
|
||||
await self.browser_controller.page.wait_for_load_state('domcontentloaded')
|
||||
# await self.browser_controller.page.wait_for_load_state('networkidle', timeout=10000)
|
||||
|
||||
self.detail_progress_bar.setValue(0)
|
||||
self.detail_progress_bar.setVisible(True)
|
||||
|
||||
# 이미지 URL 추출
|
||||
# image_urls = self.browser_controller.extract_image_urls()
|
||||
image_urls = await self.browser_controller.extract_image_urls() # 코루틴 실행
|
||||
image_urls = await self.browser_controller.extract_image_urls(self.optionHandler, is_option_data=True) # 코루틴 실행
|
||||
total_images = len(image_urls)
|
||||
self.logger.debug(f"현재 상품의 총 이미지 수 : {total_images}개")
|
||||
|
||||
|
|
@ -560,7 +560,7 @@ class TranslationApp(QWidget):
|
|||
self.logger.debug('번역 작업이 중단되었습니다.')
|
||||
break
|
||||
|
||||
await self.whale_translator.translate_image(url)
|
||||
self.whale_translator.translate_image(url)
|
||||
await self.browser_controller.paste_image_in_chrome(self.clipboardImageManager, url)
|
||||
self.update_detail_progress(i,total_images)
|
||||
|
||||
|
|
@ -576,7 +576,7 @@ class TranslationApp(QWidget):
|
|||
async def edit_option(self, product_name):
|
||||
# 상세페이지 탭 클릭
|
||||
await self.browser_controller.click_option_tab()
|
||||
await self.browser_controller.page.wait_for_load_state('domcontentloaded')
|
||||
# await self.browser_controller.page.wait_for_load_state('networkidle', timeout=10000)
|
||||
self.detail_progress_bar.setVisible(True)
|
||||
|
||||
# 옵션 최대선택갯수
|
||||
|
|
|
|||
2
main.py
2
main.py
|
|
@ -48,7 +48,7 @@ async def main():
|
|||
logger.error(f"DPI 인식 설정 실패: {e}")
|
||||
|
||||
# 기존 TranslationApp UI 사용
|
||||
whale_translator = WhaleTranslator(app, logger, secret_mode=True,vd_mode=True) # 디버그 모드 켜기
|
||||
whale_translator = WhaleTranslator(app, logger, secret_mode=True, vd_mode=True) # 디버그 모드 켜기
|
||||
await whale_translator.start_whale_browser()
|
||||
window = TranslationApp(logger, whale_translator) # PySide6 UI
|
||||
window.show()
|
||||
|
|
|
|||
55
option.py
55
option.py
|
|
@ -134,14 +134,18 @@ class OptionHandler:
|
|||
"""
|
||||
try:
|
||||
self.logger.debug(f"상품명: {product_name}에 대한 옵션을 처리 중...")
|
||||
|
||||
self.logger.debug(f"휠스크롤 Down")
|
||||
await self.browser_controller.scroll_with_wheel('down')
|
||||
self.logger.debug(f"휠스크롤 Up")
|
||||
await self.browser_controller.scroll_with_wheel('up')
|
||||
|
||||
await self.page.wait_for_load_state('domcontentloaded')
|
||||
self.logger.debug(f"동적요소 로딩완료")
|
||||
# self.logger.debug(f"포커스를 위해 클릭1회")
|
||||
# self.low_order_click()
|
||||
|
||||
# self.logger.debug(f"휠스크롤 Down")
|
||||
# await self.browser_controller.scroll_with_wheel('down')
|
||||
# self.logger.debug(f"휠스크롤 Up")
|
||||
# await self.browser_controller.scroll_with_wheel('up')
|
||||
|
||||
# await self.page.wait_for_load_state('domcontentloaded')
|
||||
# self.logger.debug(f"동적요소 로딩완료")
|
||||
await asyncio.sleep(2)
|
||||
|
||||
# 1. 단일 옵션인지 판단
|
||||
if await self.is_single_option():
|
||||
|
|
@ -210,6 +214,10 @@ class OptionHandler:
|
|||
|
||||
# # 8. 정리된 옵션을 다시한번 더 가격 낮은 순으로 정렬 클릭
|
||||
# await self.low_order_click()
|
||||
|
||||
# 9. A-Z 버튼 클릭
|
||||
self.logger.debug("A-Z 버튼을 클릭합니다.")
|
||||
self.AtoZ_button_click()
|
||||
|
||||
# 9. 저장 버튼 클릭
|
||||
self.logger.debug("저장 버튼을 클릭합니다.")
|
||||
|
|
@ -554,16 +562,14 @@ class OptionHandler:
|
|||
self.logger.error(f"옵션 필터링 및 조정 중 오류 발생: {e}", exc_info=True)
|
||||
|
||||
|
||||
async def adjust_options(self, filtered_options, max_option_count):
|
||||
async def adjust_options(self, filtered_option_names, max_option_count):
|
||||
"""
|
||||
필터링된 옵션에 맞게 체크박스 상태를 조정하는 메서드.
|
||||
:param filtered_options: 필터링된 옵션 리스트
|
||||
:param max_option_count: 최대 선택 가능한 옵션 수
|
||||
"""
|
||||
|
||||
try:
|
||||
# 필터링된 옵션들만 체크박스 상태를 조정
|
||||
filtered_option_names = {option['name'] for option in filtered_options}
|
||||
|
||||
# 옵션 체크 상태를 수집한 정보에서 필터링된 옵션들만 체크 상태로 유지
|
||||
for i, name in enumerate(self.option_info['original_names'].values()):
|
||||
checkbox = self.option_info['checkboxes'][i]
|
||||
|
|
@ -581,27 +587,32 @@ class OptionHandler:
|
|||
await checkbox.click()
|
||||
self.option_info['checked_states'][name] = False # 체크 상태 업데이트
|
||||
|
||||
# 필터링된 옵션 중 max_option_count가 넘지 않도록 체크 상태 유지
|
||||
if len(filtered_option_names) > max_option_count:
|
||||
self.logger.debug(f"필터링된 옵션이 {max_option_count}개 이상이므로 초과된 옵션을 체크 해제합니다.")
|
||||
filtered_options_sorted = sorted(filtered_option_names) # 정렬된 옵션 리스트
|
||||
for i, name in enumerate(filtered_options_sorted[max_option_count:], start=max_option_count):
|
||||
checkbox = self.option_info['checkboxes'][i]
|
||||
if self.option_info['checked_states'][name]: # 이미 체크된 경우만 해제
|
||||
self.logger.debug(f"{name} 옵션 체크 해제 (초과된 옵션)")
|
||||
await checkbox.click()
|
||||
self.option_info['checked_states'][name] = False # 체크 상태 업데이트
|
||||
# # 필터링된 옵션 중 max_option_count가 넘지 않도록 체크 상태 유지
|
||||
# if len(filtered_option_names) > max_option_count:
|
||||
# self.logger.debug(f"필터링된 옵션이 {max_option_count}개 이상이므로 초과된 옵션을 체크 해제합니다.")
|
||||
# filtered_options_sorted = sorted(filtered_option_names) # 정렬된 옵션 리스트
|
||||
# for i, name in enumerate(filtered_options_sorted[max_option_count:], start=max_option_count):
|
||||
# checkbox = self.option_info['checkboxes'][i]
|
||||
# if self.option_info['checked_states'][name]: # 이미 체크된 경우만 해제
|
||||
# self.logger.debug(f"{name} 옵션 체크 해제 (초과된 옵션)")
|
||||
# await checkbox.click()
|
||||
# self.option_info['checked_states'][name] = False # 체크 상태 업데이트
|
||||
|
||||
self.logger.debug(f"옵션 체크 상태 조정 완료.")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"옵션 체크 상태 조정 중 오류 발생: {e}", exc_info=True)
|
||||
|
||||
async def AtoZ_button_click(self):
|
||||
self.logger.debug("가격 낮은 순 정렬을 클릭합니다.")
|
||||
await self.page.click('button:has-text("A-Z")')
|
||||
# atoz_button_xpath = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[4]/div[1]/div[1]/div/div[1]/button'
|
||||
|
||||
|
||||
async def low_order_click(self):
|
||||
self.logger.debug("가격 낮은 순 정렬을 클릭합니다.")
|
||||
await self.page.click('button:has-text("가격 낮은 순")')
|
||||
await self.page.wait_for_load_state('domcontentloaded')
|
||||
# await self.page.wait_for_load_state('domcontentloaded')
|
||||
|
||||
async def save_option(self):
|
||||
"""옵션 수정 후 저장 버튼 클릭"""
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 314 KiB |
|
|
@ -31,29 +31,66 @@ class WhaleTranslator:
|
|||
# Whale 브라우저 실행
|
||||
whale_path = r"C:\\Program Files\\Naver\\Naver Whale\\Application\\whale.exe"
|
||||
process = subprocess.Popen([whale_path, '--incognito'])
|
||||
time.sleep(2)
|
||||
self.whale_pid = process.pid
|
||||
self.logger.debug(f"Whale 브라우저 실행, PID: {self.whale_pid}")
|
||||
|
||||
# 창을 찾기 전 대기 (최대 10초 동안 반복 시도)
|
||||
wait_time = 0
|
||||
self.whale_hwnd = None
|
||||
while self.whale_hwnd is None and wait_time < 10:
|
||||
self.whale_hwnd = win32gui.FindWindow(None, self.whale_window_name)
|
||||
if self.whale_hwnd:
|
||||
self.logger.debug(f"Whale 창을 찾았습니다: {self.whale_hwnd}")
|
||||
break
|
||||
time.sleep(1)
|
||||
wait_time += 1
|
||||
self.logger.debug(f"Whale 창을 찾지 못했습니다. 재시도 중... ({wait_time}초 경과)")
|
||||
|
||||
# await self.enter_url(self.newtab, change=True)
|
||||
|
||||
await asyncio.sleep(2) # 브라우저가 실행될 때까지 대기
|
||||
|
||||
hwnd = win32gui.FindWindow(None, self.whale_window_name)
|
||||
if hwnd:
|
||||
win32gui.ShowWindow(hwnd, win32con.SW_NORMAL)
|
||||
win32gui.SetWindowPos(hwnd, None, 0, 0, 1920, 1080, win32con.SWP_NOZORDER)
|
||||
else:
|
||||
# 창을 찾지 못한 경우 처리
|
||||
if not self.whale_hwnd:
|
||||
self.logger.debug("Whale 창을 찾을 수 없습니다.")
|
||||
return
|
||||
|
||||
# 창 크기 조절 및 포커스 이동
|
||||
win32gui.ShowWindow(self.whale_hwnd, win32con.SW_NORMAL)
|
||||
win32gui.SetWindowPos(self.whale_hwnd, None, 0, 0, 1920, 1080, win32con.SWP_NOZORDER)
|
||||
self.logger.debug("Whale 창 크기 조절 완료")
|
||||
|
||||
# 주소창으로 이동 후 URL 입력
|
||||
pyautogui.hotkey('ctrl', 'l')
|
||||
time.sleep(0.4)
|
||||
# pyautogui.typewrite('https://daum.net')
|
||||
self.change_lang()
|
||||
self.enter_url("about:newtab", change=True)
|
||||
self.logger.debug("URL 입력 완료")
|
||||
|
||||
# 가상 데스크탑 복귀 처리
|
||||
if self.vd_mode:
|
||||
self.return_to_virtual_desktop_1() # 가상 데스크탑 1로 복귀
|
||||
|
||||
|
||||
|
||||
def find_whale_window(self):
|
||||
"""웨일 창 핸들을 찾는 메서드"""
|
||||
if not self.whale_hwnd:
|
||||
self.whale_hwnd = self.find_window_by_title(self.whale_window_name)
|
||||
return self.whale_hwnd
|
||||
|
||||
def find_window_by_title(self, window_name):
|
||||
def enum_windows_callback(hwnd, result):
|
||||
if win32gui.IsWindowVisible(hwnd) and window_name in win32gui.GetWindowText(hwnd):
|
||||
result.append(hwnd)
|
||||
result = []
|
||||
win32gui.EnumWindows(enum_windows_callback, result)
|
||||
return result[0] if result else None
|
||||
|
||||
|
||||
def find_whale_window_pid(self):
|
||||
"""프로세스 ID를 기반으로 웨일 창 핸들을 찾는 메서드"""
|
||||
if not self.whale_hwnd:
|
||||
self.whale_hwnd = self.find_window_by_pid(self.whale_pid)
|
||||
return self.whale_hwnd
|
||||
|
||||
|
||||
def find_window_by_pid(self, pid):
|
||||
"""PID와 창 제목을 통해 유일한 웨일 창을 찾는 메서드"""
|
||||
def enum_windows_callback(hwnd, pid_list):
|
||||
|
|
@ -134,7 +171,7 @@ class WhaleTranslator:
|
|||
except Exception as e:
|
||||
self.logger.debug(f"가상 데스크톱 전환 중 오류 발생: {e}", exc_info=True)
|
||||
|
||||
async def translate_image(self, url, path=None):
|
||||
def translate_image(self, url, path=None):
|
||||
|
||||
if self.vd_mode:
|
||||
self.switch_to_virtual_desktop_2()
|
||||
|
|
@ -143,15 +180,14 @@ class WhaleTranslator:
|
|||
if not self.whale_hwnd:
|
||||
# 웨일 창을 찾지 못했을 경우 사용자에게 입력 받기
|
||||
self.logger.debug("웨일 창을 찾지 못했습니다. 계속하려면 'y'를 입력하세요.")
|
||||
user_input = input("계속하려면 'y'를 입력하세요: ").lower()
|
||||
# user_input = input("계속하려면 'y'를 입력하세요: ").lower()
|
||||
|
||||
if user_input != 'y':
|
||||
self.logger.debug("사용자에 의해 작업이 중단되었습니다.")
|
||||
raise SystemExit("프로그램이 사용자 요청으로 종료되었습니다.") # 프로그램 종료
|
||||
else:
|
||||
self.logger.debug("새 웨일 창을 생성합니다.")
|
||||
await self.create_and_update_whale_window()
|
||||
|
||||
# if user_input != 'y':
|
||||
# self.logger.debug("사용자에 의해 작업이 중단되었습니다.")
|
||||
# raise SystemExit("프로그램이 사용자 요청으로 종료되었습니다.") # 프로그램 종료
|
||||
# else:
|
||||
# self.logger.debug("새 웨일 창을 생성합니다.")
|
||||
self.create_and_update_whale_window()
|
||||
|
||||
|
||||
if self.whale_hwnd:
|
||||
|
|
@ -161,20 +197,27 @@ class WhaleTranslator:
|
|||
pyautogui.moveTo(960,580) # 마우스 센터로 이동
|
||||
|
||||
# pyautogui.hotkey('ctrl', 'l') # 웨일 브라우저의 주소창으로 이동
|
||||
await self.enter_url(url)
|
||||
await asyncio.sleep(1) # 페이지 로딩 대기
|
||||
self.enter_url(url)
|
||||
pyautogui.press('enter')
|
||||
# await asyncio.sleep(1) # 페이지 로딩 대기
|
||||
time.sleep(2)
|
||||
|
||||
pyautogui.rightClick()
|
||||
await asyncio.sleep(0.2) # 페이지 로딩 대기
|
||||
# await asyncio.sleep(0.2) # 페이지 로딩 대기
|
||||
time.sleep(2)
|
||||
|
||||
pyautogui.press('r') # 번역 클릭
|
||||
await asyncio.sleep(7) # 페이지 로딩 대기
|
||||
# await asyncio.sleep(7) # 페이지 로딩 대기
|
||||
time.sleep(7)
|
||||
|
||||
pyautogui.rightClick()
|
||||
await asyncio.sleep(0.2) # 페이지 로딩 대기
|
||||
# await asyncio.sleep(0.2) # 페이지 로딩 대기
|
||||
time.sleep(0.2)
|
||||
|
||||
pyautogui.press('c') # 번역된 이미지 클립보드에 복사
|
||||
# pyautogui.hotkey('ctrl', 'l') # 새 탭으로 이동
|
||||
# pyautogui.typewrite(self.newtab) # URL을 입력
|
||||
await self.enter_url(self.newtab)
|
||||
self.enter_url(self.newtab)
|
||||
self.logger.debug(f'번역 완료: {url}')
|
||||
|
||||
if self.vd_mode:
|
||||
|
|
@ -187,7 +230,7 @@ class WhaleTranslator:
|
|||
else:
|
||||
self.logger.debug('웨일 창을 찾을 수 없습니다.')
|
||||
|
||||
async def create_and_update_whale_window(self):
|
||||
def create_and_update_whale_window(self):
|
||||
"""
|
||||
웨일 창을 찾지 못했을 경우 새 웨일 창을 생성하고 핸들을 업데이트하는 메서드.
|
||||
"""
|
||||
|
|
@ -199,7 +242,8 @@ class WhaleTranslator:
|
|||
self.whale_pid = process.pid
|
||||
self.logger.debug(f"새 웨일 브라우저 실행, PID: {self.whale_pid}")
|
||||
|
||||
await asyncio.sleep(2) # 브라우저가 실행될 때까지 대기
|
||||
# await asyncio.sleep(2) # 브라우저가 실행될 때까지 대기
|
||||
time.sleep(2)
|
||||
|
||||
# 창 핸들 업데이트
|
||||
self.whale_hwnd = self.find_whale_window()
|
||||
|
|
@ -240,26 +284,31 @@ class WhaleTranslator:
|
|||
# # 한글로 변경
|
||||
# user32.LoadKeyboardLayoutW('00000412', 1)
|
||||
|
||||
async def enter_url(self, url, change=False):
|
||||
if change:
|
||||
# 언어 전환이 완료되었는지 확인하는 함수
|
||||
async def wait_for_language_switch():
|
||||
# 최대 3초까지 언어 전환 확인 시도 (0.1초 간격)
|
||||
for _ in range(30):
|
||||
if KO_EN.get_hanguel_state() == 0: # 영어로 전환된 상태가 0
|
||||
return True
|
||||
await asyncio.sleep(0.1)
|
||||
return False
|
||||
def change_lang(self):
|
||||
# 언어 전환이 완료되었는지 확인하는 함수
|
||||
def wait_for_language_switch():
|
||||
# 최대 3초까지 언어 전환 확인 시도 (0.1초 간격)
|
||||
for _ in range(30):
|
||||
if KO_EN.get_hanguel_state() == 0: # 영어로 전환된 상태가 0
|
||||
return True
|
||||
time.sleep(0.1)
|
||||
# await asyncio.sleep(0.1)
|
||||
return False
|
||||
|
||||
# 주소창에 URL 입력하기 전에 영어로 변경
|
||||
KO_EN.set_keyboard_language('eng')
|
||||
# 주소창에 URL 입력하기 전에 영어로 변경
|
||||
KO_EN.set_keyboard_language('eng')
|
||||
|
||||
# 언어 전환 완료 대기
|
||||
is_switched = await wait_for_language_switch()
|
||||
# 언어 전환 완료 대기
|
||||
is_switched = wait_for_language_switch()
|
||||
|
||||
if not is_switched:
|
||||
print("영어로 전환하는데 실패했습니다.")
|
||||
return
|
||||
if not is_switched:
|
||||
self.logger.debug("영어로 전환하는데 실패했습니다.")
|
||||
return
|
||||
else:
|
||||
self.logger.debug("전환 성공")
|
||||
|
||||
|
||||
def enter_url(self, url, change=False):
|
||||
|
||||
# 언어 전환이 완료되면 주소창으로 이동 후 URL 입력
|
||||
pyautogui.hotkey('ctrl', 'l') # 주소창으로 이동
|
||||
|
|
@ -296,7 +345,7 @@ class WhaleTranslator:
|
|||
except Exception as e:
|
||||
self.logger.error(f"웨일 창을 종료하는 중 오류 발생: {e}", exc_info=True)
|
||||
|
||||
async def close_all_virtual_desktops(self):
|
||||
def close_all_virtual_desktops(self):
|
||||
"""모든 가상 데스크톱을 종료"""
|
||||
try:
|
||||
desktops = get_virtual_desktops()
|
||||
|
|
|
|||
Loading…
Reference in New Issue