캡차 해결코드 추가

This commit is contained in:
EnvyPC 2024-04-16 07:52:07 +09:00
parent 4dae661b7e
commit fff54b66ef
11 changed files with 215 additions and 72 deletions

BIN
TaoSourcerer.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

BIN
TaoSourcerer.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

View File

@ -9,7 +9,7 @@ import logging
# 로거 인스턴스 가져오기
logger = logging.getLogger('default_logger')
def automatch(db, item_count, sort_order, progress_callback=None):
def automatch(db, item_count, message_controller, sort_order, progress_callback=None):
logger.debug(f"automatch Start 변수 : [DB] - {db}, [item_count] - {item_count}, [sort_order] - {sort_order}")
# 데이터베이스에서 필요한 데이터 로드
@ -86,7 +86,7 @@ def automatch(db, item_count, sort_order, progress_callback=None):
products = [] # 타오서칭함수 호출 전 초기화
# fetch_and_save_taobao_products 함수 호출
# products = fetch_and_save_taobao_products(driver, imgurl, item_count=len(group), sort_order=1)
products = fetch_and_save_taobao_products(driver, imgurl, cursor, db, item_count=5, sort_order=1)
products = fetch_and_save_taobao_products(driver, message_controller, imgurl, cursor, db, item_count=5, sort_order=1)
products_count = len(products)
logger.debug(f"가져온 products_count : {products_count}")
# logger.debug(f"automatch - for 구문 내 fetch_and_save_taobao_products 완료 후 리턴된 products : {products}")
@ -161,16 +161,33 @@ def automatch(db, item_count, sort_order, progress_callback=None):
SET MatchingCat = ?, delvFee = ?, packingFee = ?, plusFee = ?, MatchingUrl = ?, tao_imageUrl = ?, tao_itemID = ?
WHERE id = ?
"""
# 메인 키워드 업데이트 조건 검사 및 적용
if index == 0 or row.keyword != main_keyword:
# 첫 번째 상품(메인 키워드) 또는 메인 키워드와 다른 추가 키워드에 대한 업데이트 실행
if index == 0:
logger.debug(f"첫 번째 상품 업데이트 실행: {row.keyword}")
cursor.execute(update_ns_query, (final_category, delvFee, packingFee, plusFee, pc_url, imageUrl, itemID, ns_id))
conn.commit()
logger.debug(f"NaverShopping id {ns_id} 업데이트 완료: {itemUrl}, {itemID}, {imageUrl}, {item_name}, {price}, {sales_volume}")
else:
# 메인 키워드와 동일할 경우 업데이트 생략
logger.debug(f"NaverShopping id {ns_id} 업데이트 생략: 메인 키워드와 동일")
# 이후 인덱스에 대해서는 기존 조건에 따라 업데이트를 결정
if row.keyword != "-" and row.keyword != main_keyword:
logger.debug(f"추가 키워드 업데이트 실행: {row.keyword}")
cursor.execute(update_ns_query, (final_category, delvFee, packingFee, plusFee, pc_url, imageUrl, itemID, ns_id))
conn.commit()
logger.debug(f"NaverShopping id {ns_id} 업데이트 완료: {itemUrl}, {itemID}, {imageUrl}, {item_name}, {price}, {sales_volume}")
else:
logger.debug(f"NaverShopping id {ns_id} 업데이트 생략: {row.keyword}")
# # 메인 키워드 업데이트 조건 검사 및 적용
# if index == 0 or row.keyword != "-" or row.keyword != main_keyword:
# logger.debug(f"row.keyword : {row.keyword}")
# # 첫 번째 상품(메인 키워드) 또는 메인 키워드와 다른 추가 키워드에 대한 업데이트 실행
# cursor.execute(update_ns_query, (final_category, delvFee, packingFee, plusFee, pc_url, imageUrl, itemID, ns_id))
# conn.commit()
# logger.debug(f"NaverShopping id {ns_id} 업데이트 완료: {itemUrl}, {itemID}, {imageUrl}, {item_name}, {price}, {sales_volume}")
# elif row.keyword == main_keyword or row.keyword == "-":
# logger.debug(f"row.keyword : {row.keyword}")
# # 메인 키워드와 동일할 경우 업데이트 생략
# logger.debug(f"NaverShopping id {ns_id} 업데이트 생략: 메인 키워드와 동일")
# index = index + 1

View File

@ -29,11 +29,14 @@ def load_cookies(driver):
del cookie['expiry']
driver.add_cookie(cookie)
logger.debug("쿠키 로드 완료")
return True
except EOFError as eof_error:
logger.debug(f"쿠키 로드 중 EOF 에러 발생 : {eof_error}")
return False
except Exception as e:
logger.debug(f"쿠키 로드 중 기타 에러 발생 : {e}")
return False
def check_login_status_ori(driver):
# 로그인되지 않은 상태의 XPath
not_logged_in_xpath = "//li[@id='J_SiteNavLogin']/div/div/a"

View File

@ -0,0 +1,39 @@
# from src.pushbullet import Pushbullet
# from src.telegram import Telegram
# from src.email import Email
# from src.sms import SMS
# from src.synology_chat import SynologyChat
# from src.naver_line import NaverLine
import logging
logging.basicConfig(level=logging.INFO)
class MessageController:
def __init__(self, method, **credentials):
self.method = method.lower()
self.credentials = credentials
self.message_service = self.create_service()
def create_service(self):
# if self.method == 'pushbullet':
# return Pushbullet(self.credentials['access_token'])
# elif self.method == 'telegram':
# return Telegram(self.credentials['api_key'])
# elif self.method == 'email':
# return Email(self.credentials['user'], self.credentials['password'])
# elif self.method == 'sms':
# return SMS(self.credentials['api_key'])
# elif self.method == 'synology_chat':
# return SynologyChat(self.credentials['url'], self.credentials['token'])
# elif self.method == 'naver_line':
# return NaverLine(self.credentials['access_token'])
# else:
# raise ValueError("지원하지 않는 메시지 전송 방식입니다.")
return None
# def send_message(self, title, body):
# print(f"{self.method.title()}를 통해 메시지를 보내는 중...")
# self.message_service.send_message(title, body)
# def test_message(self):
# self.send_message("테스트 타이틀", "테스트 본문입니다.")

View File

@ -64,6 +64,57 @@ def trans(text):
logger.debug(f"번역 중 오류발생 : {e}")
def slide_capcha(driver):
driver.switch_to.frame('baxia-dialog-content')
print("슬라이딩 시도")
try:
while True:
# 슬라이더 캡차가 로드되기를 기다림
slider = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, 'nc_1_n1z')) # 슬라이더의 ID를 정확히 알아야 함
)
print("슬라이더 로드")
# 슬라이더를 클릭하여 활성화
ActionChains(driver).click_and_hold(slider).perform()
print("슬라이더 클릭")
# 슬라이더를 움직이는 로직 (예시: 오른쪽으로 100 픽셀)
ActionChains(driver).move_by_offset(248, 0).perform() # 필요한 경우 이 값을 조정
print("슬라이더 이동")
time.sleep(1.5) # 결과 확인을 위한 대기 시간
# 마우스 버튼을 놓음
ActionChains(driver).move_by_offset(10, 0).perform() # 필요한 경우 이 값을 조정
ActionChains(driver).release().perform()
print("슬라이더 버튼 놓음")
# 성공적으로 슬라이드 했는지 확인
print("캡차 결과 확인 중...")
time.sleep(2) # 결과 확인을 위한 대기 시간
# 캡차 해결 여부 확인
#<div id="`nc_1_refresh1`" class="errloading"><i id="`nc_1_refresh2`" class="nc_iconfont icon_warn"></i>Oops... something's wrong. Please refresh and try again.(error:x5fZg)</div>
try:
refresh_button = WebDriverWait(driver, 3).until(
EC.visibility_of_element_located((By.CLASS_NAME, 'errloading'))
)
print("CAPTCHA 해결 실패, 재시도 중...")
# refresh_button.click()
ActionChains(driver).click(refresh_button).perform()
except NoSuchElementException:
# 새로 고침 버튼이 없으면 캡차 해결 성공으로 간주
print("CAPTCHA 해결됨.")
break
finally:
# 브라우저 닫기
driver.switch_to.default_content()
driver.refresh()
print("슬라이드 캡차 해결 완료.")
def setup_driver():
"""
@ -74,11 +125,32 @@ def setup_driver():
logger.debug("웹드라이버 설정")
options = webdriver.ChromeOptions()
ua = UserAgent()
ua.user_agent_list = ua.pc_browsers
# ua = UserAgent()
# ua.user_agent_list = ua.pc_browsers
# # 랜덤 PC Agent 선택
# random_PC_UA = ua.random
pc_user_agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15"
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0"
"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36 Edg/88.0.705.63"
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36"
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0"
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b"
]
# 랜덤 PC 사용자 에이전트 선택
random_PC_UA = random.choice(pc_user_agents)
# 랜덤 PC Agent 선택
random_PC_UA = ua.random
logger.debug(f"UserAgent : {random_PC_UA}")
options.add_argument(f"--user-agent={random_PC_UA}") # 랜덤 user_agent 사용
@ -122,7 +194,7 @@ def login_and_manage_session(driver):
# 로그인 상태 확인 루프
while True:
if check_login_status(driver):
logger.debug("로그인 성공!")
logger.debug("로그인 성공! 서치 웹페이지로 이동")
driver.get("https://world.taobao.com/wow/tmg-fc/tmw/search_image?")
break # 로그인이 확인되면 루프 탈출
else:
@ -134,18 +206,18 @@ def login_and_manage_session(driver):
driver.get("https://world.taobao.com/wow/z/oversea/SEO-SEM/ovs-pc-login?redirectURL=https%3A%2F%2Fworld.taobao.com%2Fwow%2Ftmg-fc%2Ftmw%2Fsearch_image%3F")
while True:
if check_login_status(driver):
logger.debug("로그인 성공!")
logger.debug("새로운 로그인 성공! -> 서치 웹페이지로 이동")
driver.get("https://world.taobao.com/wow/tmg-fc/tmw/search_image?")
break # 로그인이 확인되면 루프 탈출
else:
logger.debug("로그인이 확인되지 않았습니다. 재로그인을 시도해주세요.")
logger.debug("새로운 로그인이 확인되지 않았습니다. 재로그인을 시도해주세요.")
# driver.get("https://world.taobao.com/wow/z/oversea/SEO-SEM/ovs-pc-login?redirectURL=https%3A%2F%2Fworld.taobao.com%2Fwow%2Ftmg-fc%2Ftmw%2Fsearch_image%3F")
time.sleep(5) # 재시도 전에 대기
# 로그인 완료 후 쿠키 저장
save_cookies(driver)
def fetch_and_save_taobao_products(driver, imgurl, cursor, db, item_count=5, sort_order=1):
def fetch_and_save_taobao_products(driver, message_controller, imgurl, cursor, db, item_count=5, sort_order=1):
"""
타오바오의 상품을 검색하고 결과를 파싱하는 함수
"""
@ -287,23 +359,23 @@ def fetch_and_save_taobao_products(driver, imgurl, cursor, db, item_count=5, sor
except Exception as e:
logger.debug(f"과정 중 에러 발생 : {e}")
def search_img(imgurl):
# imgurl에서 이미지를 로컬에 저장
local_image_path = "./img/temp_image.jpg"
if not os.path.exists("./img"):
os.makedirs("./img")
urlretrieve(imgurl, local_image_path) # 주어진 imgurl 사용
# def search_img(imgurl):
# # imgurl에서 이미지를 로컬에 저장
# local_image_path = "./img/temp_image.jpg"
# if not os.path.exists("./img"):
# os.makedirs("./img")
# urlretrieve(imgurl, local_image_path) # 주어진 imgurl 사용
# JavaScript를 사용하여 이미지 검색 버튼 클릭
search_button_selector = ".component-search-icon-active"
driver.execute_script(f"document.querySelector('{search_button_selector}').click();")
logger.debug("이미지검색버튼 클릭")
# # JavaScript를 사용하여 이미지 검색 버튼 클릭
# search_button_selector = ".component-search-icon-active"
# driver.execute_script(f"document.querySelector('{search_button_selector}').click();")
# logger.debug("이미지검색버튼 클릭")
# 파일 업로드 처리
file_input = WebDriverWait(driver, 60).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "input[type='file']"))
)
file_input.send_keys(os.path.abspath(local_image_path))
# # 파일 업로드 처리
# file_input = WebDriverWait(driver, 60).until(
# EC.presence_of_element_located((By.CSS_SELECTOR, "input[type='file']"))
# )
# file_input.send_keys(os.path.abspath(local_image_path))
def check_first_product(driver):
try:
@ -326,32 +398,41 @@ def fetch_and_save_taobao_products(driver, imgurl, cursor, db, item_count=5, sor
except TimeoutException:
return False
def handle_captcha(driver):
capcha_iframe = 'baxia-dialog-content'
def handle_captcha(driver, message_controller):
captcha_iframe_id = 'baxia-dialog-content'
last_message_time = time.time()
# iframe=baxia-dialog-content
# id=baxia-punish
# class=baxia-punish captchacapslidev2 pc-ajax
# 단순드래그
# id="_oid_ifr_"
captcha_image_xpath = "//img[@class='captcha-img']"
try:
WebDriverWait(driver, 5).until(EC.visibility_of_element_located((By.XPATH, capcha_iframe)))
logger.debug("CAPTCHA화면 발생. 진행을 위해 해결하세요")
WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, captcha_iframe_id)))
logger.debug("CAPTCHA 화면 발생. 진행을 위해 해결하세요")
# 사용자에게 초기 캡차 발생 알림 보내기
# message_controller.send_message("CAPTCHA가 발생했습니다. 해결해 주세요.")
print("최초 캡챠발생 : 사용자에게 알람 발송")
slide_capcha(driver)
# iframe으로 포커스 전환
driver.switch_to.frame(captcha_iframe_id)
while True:
if check_first_product(driver):
logger.debug("CAPTCHA가 해결되었습니다. 첫번째 상품이 로드되었습니다.")
return True
else:
logger.debug("CAPTCHA가 해결될때 까지 기다림.")
time.sleep(25)
time.sleep(5) # 30초마다 캡차 상태 검사
current_time = time.time()
# if check_this_page(driver, found_first_product, message_controller):
if handle_sorry_message(driver):
driver.refresh()
if check_first_product(driver): # 캡차가 해결되었는지 체크
logger.debug("CAPTCHA가 해결되었습니다. 첫 번째 상품이 로드되었습니다.")
driver.switch_to.default_content()
return True
if current_time - last_message_time >= 600: # 10분마다 사용자에게 메시지 재전송
# message_controller.send_message("CAPTCHA 해결이 여전히 필요합니다. 확인해 주세요.")
print("CAPTCHA 해결이 여전히 필요합니다. 확인해 주세요. : 사용자에게 알람 발송")
last_message_time = current_time # 메시지 전송 시간 업데이트
except TimeoutException:
logger.error("CAPTCHA 화면을 찾는 데 실패했습니다.")
return False
while True: # 무한 루프를 시작하여 조건에 따라 재시도를 관리
search_attempts = 0
@ -376,17 +457,17 @@ def fetch_and_save_taobao_products(driver, imgurl, cursor, db, item_count=5, sor
found_first_product = True
break # 첫 번째 상품을 찾았으니 내부 while 루프 탈출
else:
if handle_sorry_message(driver) and refresh_attempts < max_refresh_attempts:
if handle_captcha(driver, message_controller):
logger.debug("캡차 화면입니다. 캡차를 해결합니다.")
# handle_captcha(driver)
elif handle_sorry_message(driver) and refresh_attempts < max_refresh_attempts:
logger.debug("Sorry 화면입니다. 페이지를 새로고침합니다.")
# driver.refresh()
# time.sleep(3)
refresh_attempts += 1
elif handle_captcha(driver):
logger.debug("캡차 화면입니다. 캡차를 해결합니다.")
# handle_captcha(driver)
elif check_login_status(driver):
logger.debug("로그인이 필요한 화면입니다. 로그인을 확인합니다.")
# check_login_status(driver)
# elif check_login_status(driver):
# logger.debug("로그인이 필요한 화면입니다. 로그인을 확인합니다.")
# # check_login_status(driver)
else:
logger.debug("알 수 없는 상태입니다. 상품 검색을 다시 시도합니다.")
break # 알 수 없는 상태이므로 내부 while 루프를 탈출하여 상품 검색을 재시도

View File

@ -174,26 +174,23 @@ def parse_naver_shopping(keyword_id, keyword, isBranch, branchCount, json_data,
if not isBranch:
logger.debug(f"가지치기 설정이 [{isBranch}] 이므로 연관검색어 초기화")
related_tags = [""] * 5 # 5개의 빈 문자열로 초기화
related_tags_ori = [""] * 5 # related_tags_ori 리스트도 동일하게 초기화 필요시 추가
related_tags = ["-"] * 5 # 5개의 빈 문자열로 초기화
else:
logger.debug(f"가지치기 설정이 [{isBranch}] 이므로 {branchCount}개 연관검색어 보존")
# branchCount의 수만큼 원래 값을 유지하고, 나머지는 빈칸으로 채움
if branchCount < len(related_tags):
related_tags = related_tags[:branchCount] + [""] * (5 - branchCount)
related_tags_ori = related_tags_ori[:branchCount] + [""] * (5 - branchCount)
related_tags = related_tags[:branchCount] + ["-"] * (5 - branchCount)
else:
# branchCount가 related_tags의 길이보다 크거나 같은 경우, 모든 값을 그대로 유지
related_tags = related_tags[:5] # 리스트 길이를 5로 제한
related_tags_ori = related_tags_ori[:5] # related_tags_ori도 동일하게 처리
# DB에 기록하기
for index, product in enumerate(final_top_5_products):
# 첫 번째 제품에 대한 keyword 처리
logger.debug(f"{keyword}의 검색결과 [{index+1}]번째 상품 처리")
set_keyword = keyword if index == 0 else related_tags_ori[index - 1] if index - 1 < len(related_tags_ori) else keyword
current_keyword = set_keyword
logger.debug(f"가지치기 상태 : [{isBranch}] 이므로 {index+1}번째 [current_keyword]에는 [{set_keyword}] 할당 ")
# current_keyword = keyword if index == 0 else related_tags[index - 1] if index - 1 < len(related_tags) else keyword
current_keyword = keyword if index == 0 else related_tags[index - 1] if index - 1 < len(related_tags) else ""
logger.debug(f"가지치기 상태 : [{isBranch}] 이므로 {index+1}번째 [current_keyword]에는 [{current_keyword}] 할당 ")
# current_keyword = keyword
price = product.get("item", {}).get("price")
productTitle = product.get("item", {}).get("productTitle")

View File

@ -13,11 +13,12 @@ class TaoScrapingThread(QThread):
progress_updated = pyqtSignal(int, int)
#finished = pyqtSignal()
def __init__(self, db_name, item_count, sort_order, parent=None):
def __init__(self, db_name, item_count, message_controller, sort_order, parent=None):
super(TaoScrapingThread, self).__init__(parent)
self.db_name = db_name
self.item_count = item_count
self.sort_order = sort_order
self.message_controller = message_controller
def run(self):
def update_progress(current, total):
@ -26,7 +27,7 @@ class TaoScrapingThread(QThread):
logger.debug("Automatch 작업 시작")
try:
# automatch 함수 호출 시 콜백 함수 전달
automatch(self.db_name, self.item_count, self.sort_order, progress_callback=update_progress)
automatch(self.db_name, self.item_count, self.message_controller, self.sort_order, progress_callback=update_progress)
time.sleep(0.1) # 예시로 작업 지연을 표현합니다.
logger.debug("Automatch 작업 완료")

View File

@ -16,6 +16,7 @@ import xlwings as xw
import pyautogui
import math
from modules.message.MessageController import MessageController
from modules.text_input import TextDialog
from datetime import datetime
@ -232,7 +233,10 @@ class Ui_Dialog(QtWidgets.QDialog):
# 화면 꺼짐과 절전 모드 방지 기능 활성화
prevent_sleep_mode()
# 메세지 컨트롤러 객체 생성
message_controller = MessageController("method")
self.message_controller = message_controller
if self.logger: # logger 인스턴스가 제공되었는지 확인
self.logger.debug("Ui_Dialog 초기화 시작")
@ -459,7 +463,7 @@ class Ui_Dialog(QtWidgets.QDialog):
try:
# AutomatchThread 스레드 시작
self.automatch_thread = TaoScrapingThread(self.db_name, item_count, sort_order)
self.automatch_thread = TaoScrapingThread(self.db_name, item_count, self.message_controller, sort_order)
self.automatch_thread.progress_updated.connect(self.update_progress_bar) # 진행 상황 업데이트를 위한 연결
self.automatch_thread.finished.connect(self.on_automatch_finished) # automatch 작업 완료 후 처리
self.automatch_thread.start()

View File

@ -45,6 +45,7 @@ exe = EXE(
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon='TaoSourcerer.ico',
)
coll = COLLECT(
exe,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 204 KiB