Compare commits
11 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
5e7b430689 | |
|
|
06c01c6d90 | |
|
|
844366938d | |
|
|
c856ce4045 | |
|
|
e5e153777e | |
|
|
af186266b3 | |
|
|
d10feb6069 | |
|
|
5f530fa0c1 | |
|
|
25e4f1643f | |
|
|
e5c16c42cd | |
|
|
2065aa13ac |
|
|
@ -82,6 +82,7 @@ def create_db(db_name):
|
|||
tao_imageUrl TEXT,
|
||||
tao_itemID INTEGER,
|
||||
tao_localimage TEXT,
|
||||
merged_price INTEGER,
|
||||
unique_id TEXT,
|
||||
date_created TEXT DEFAULT (DATE('now', 'localtime')),
|
||||
time_created TEXT DEFAULT (TIME('now', 'localtime')),
|
||||
|
|
|
|||
|
|
@ -95,7 +95,6 @@ def automatch(db, item_count, message_controller, sort_order, progress_callback=
|
|||
for i, product in enumerate(products):
|
||||
logger.debug(f"{i}번째 product 정보 DB 업데이트 준비")
|
||||
itemUrl, itemID, imageUrl, item_name, price, sales_volume = product
|
||||
|
||||
# DB에 상품 정보 업데이트
|
||||
update_query = """
|
||||
INSERT INTO Taobao (keyword_id, itemUrl, itemID, imageUrl, item_name, price, sales_volume)
|
||||
|
|
@ -105,7 +104,7 @@ def automatch(db, item_count, message_controller, sort_order, progress_callback=
|
|||
cursor.execute(update_query, (keyword_id, itemUrl, itemID, imageUrl, item_name, price, sales_volume))
|
||||
logger.debug(f"TaoBao 테이블에 [{product}/{len(products)}] 내용 업데이트 실행")
|
||||
except Exception as e:
|
||||
logger.debug(f"product 정보 DB 업데이트 중 오류 발생 : {e}")
|
||||
logger.debug(f"product 정보 DB 업데이트 중 오류 발생 : {e}", exc_info=True)
|
||||
|
||||
logger.debug(f"{len(products)}개의 product 정보 DB 업데이트 완료")
|
||||
|
||||
|
|
@ -120,7 +119,19 @@ def automatch(db, item_count, message_controller, sort_order, progress_callback=
|
|||
# for index, (row, product) in enumerate(zip(group.iterrows(), products[:product_count])):
|
||||
for index, (row, product) in enumerate(zip(group.itertuples(index=False), products[:product_count])):
|
||||
|
||||
itemUrl, itemID, imageUrl, item_name, price, sales_volume = product
|
||||
# itemUrl, itemID, imageUrl, item_name, price, sales_volume = product # 튜플일때
|
||||
|
||||
# 상품 정보 추출 #리스트일때
|
||||
itemUrl = product['Product URL']
|
||||
itemID = product['Tao_itemID']
|
||||
imageUrl = product['Image URL']
|
||||
item_name = product['Product Name']
|
||||
price = product['Price']
|
||||
sales_volume = product['Sales Volume']
|
||||
|
||||
if price == '' and itemID =='':
|
||||
continue
|
||||
|
||||
if keyword_id != row.keyword_id:
|
||||
continue
|
||||
|
||||
|
|
@ -154,7 +165,7 @@ def automatch(db, item_count, message_controller, sort_order, progress_callback=
|
|||
# PC 주소 생성
|
||||
match = re.search(r'taobao.com/i(\d{10,12})', itemUrl)
|
||||
pc_url = f"https://item.taobao.com/item.htm?id={match.group(1)}" if match else ""
|
||||
|
||||
|
||||
# NaverShopping 테이블에 매칭 정보 업데이트
|
||||
update_ns_query = """
|
||||
UPDATE NaverShopping
|
||||
|
|
@ -162,7 +173,7 @@ def automatch(db, item_count, message_controller, sort_order, progress_callback=
|
|||
WHERE id = ?
|
||||
"""
|
||||
|
||||
if index == 0:
|
||||
if row.keyword != "-" and 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()
|
||||
|
|
|
|||
|
|
@ -37,30 +37,25 @@ def load_cookies(driver):
|
|||
logger.debug(f"쿠키 로드 중 기타 에러 발생 : {e}")
|
||||
return False
|
||||
|
||||
def check_login_status_ori(driver):
|
||||
# 로그인되지 않은 상태의 XPath
|
||||
not_logged_in_xpath = "//li[@id='J_SiteNavLogin']/div/div/a"
|
||||
# 로그인된 상태의 XPath
|
||||
# logged_in_xpath = "//li[@id='J_SiteNavLogin']/div/div[2]/a"
|
||||
def check_login_status_by_iframe(driver):
|
||||
logged_in_xpath = "/html/body/div[1]/div/ul[1]/li[2]/div[1]/div[2]/a"
|
||||
|
||||
# # 로그인되지 않은 상태 확인
|
||||
# if WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, not_logged_in_xpath))):
|
||||
# logger.debug("로그인되지 않았습니다. 로그인이 필요합니다.")
|
||||
# return False
|
||||
logger.debug("로그인된 상태 확인")
|
||||
# 로그인된 상태 확인
|
||||
if WebDriverWait(driver, 3).until(EC.presence_of_element_located((By.XPATH, logged_in_xpath))):
|
||||
user_id_text = driver.find_element(By.XPATH, logged_in_xpath).text
|
||||
logger.debug(f"로그인된 상태입니다. 로그인된 ID: {user_id_text}")
|
||||
return True
|
||||
# 로그인되지 않은 상태 확인
|
||||
elif WebDriverWait(driver, 3).until(EC.presence_of_element_located((By.XPATH, not_logged_in_xpath))):
|
||||
logger.debug("로그인되지 않았습니다. 로그인이 필요합니다.")
|
||||
return False
|
||||
else:
|
||||
logger.debug("로그인 상태를 확인할 수 없습니다.")
|
||||
return False
|
||||
while True:
|
||||
try:
|
||||
logger.debug("로그인된 상태 확인 중...")
|
||||
# 로그인된 상태 확인
|
||||
if driver.find_element(By.XPATH, logged_in_xpath):
|
||||
user_id_text = driver.find_element(By.XPATH, logged_in_xpath).text
|
||||
logger.debug(f"로그인된 상태입니다. 로그인된 ID: {user_id_text}")
|
||||
return True
|
||||
except:
|
||||
# 로그인되지 않은 상태 확인
|
||||
try:
|
||||
logger.debug("로그인되지 않았습니다. 로그인이 필요합니다.")
|
||||
time.sleep(5) # 5초 후 다시 체크
|
||||
except:
|
||||
logger.debug("로그인 상태를 확인할 수 없습니다. 잠시 후 다시 시도합니다.")
|
||||
time.sleep(5) # 예외 발생 시 5초 대기 후 재시도
|
||||
|
||||
|
||||
def check_login_status(driver):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
import cv2
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import logging
|
||||
|
||||
# 로거 인스턴스 가져오기
|
||||
logger = logging.getLogger('default_logger')
|
||||
|
||||
def pil_to_cv(image_pil):
|
||||
try:
|
||||
"""PIL 이미지를 OpenCV 이미지(numpy array)로 변환"""
|
||||
image_np = np.array(image_pil)
|
||||
# Convert RGB to BGR
|
||||
return image_np[:, :, ::-1]
|
||||
except Exception as e:
|
||||
logger.error(f"pil_to_cv 처리 중 에러 발생 : {e}")
|
||||
return image_pil
|
||||
|
||||
def cv_to_pil(image_cv):
|
||||
try:
|
||||
"""OpenCV 이미지(numpy array)를 PIL 이미지로 변환"""
|
||||
# Convert BGR to RGB
|
||||
return Image.fromarray(image_cv[:, :, ::-1])
|
||||
except Exception as e:
|
||||
logger.error(f"cv_to_pil 처리 중 에러 발생 : {e}")
|
||||
return image_cv
|
||||
|
||||
|
||||
def crop_by_boxline(image_pil, crop_percent):
|
||||
try:
|
||||
image_cv = pil_to_cv(image_pil)
|
||||
height, width = image_cv.shape[:2]
|
||||
crop_height = int(height * crop_percent)
|
||||
crop_width = int(width * crop_percent)
|
||||
start_row, start_col = crop_height // 2, crop_width // 2
|
||||
end_row, end_col = height - start_row, width - start_col
|
||||
cropped_image_cv = image_cv[start_row:end_row, start_col:end_col]
|
||||
return cv_to_pil(cropped_image_cv)
|
||||
except Exception as e:
|
||||
logger.error(f"crop_by_boxline 처리 중 에러 발생 : {e}")
|
||||
return image_pil
|
||||
|
||||
def rotate_by_angle(image_pil, angle, color):
|
||||
try:
|
||||
image_cv = pil_to_cv(image_pil)
|
||||
height, width = image_cv.shape[:2]
|
||||
center = (width // 2, height // 2)
|
||||
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
|
||||
if color == "white":
|
||||
rotated_image_cv = cv2.warpAffine(image_cv, rotation_matrix, (width, height), borderValue=(255, 255, 255))
|
||||
elif color == "transparent":
|
||||
rotated_image_cv = cv2.warpAffine(image_cv, rotation_matrix, (width, height), borderMode=cv2.BORDER_CONSTANT, borderValue=(0, 0, 0, 0))
|
||||
return cv_to_pil(rotated_image_cv)
|
||||
except Exception as e:
|
||||
logger.error(f"rotate_by_angle 처리 중 에러 발생 : {e}")
|
||||
return image_pil
|
||||
|
||||
def mirror_by_image(image_pil):
|
||||
try:
|
||||
image_cv = pil_to_cv(image_pil)
|
||||
flipped_image_cv = cv2.flip(image_cv, 1)
|
||||
return cv_to_pil(flipped_image_cv)
|
||||
except Exception as e:
|
||||
logger.error(f"mirror_by_image 처리 중 에러 발생 : {e}")
|
||||
return image_pil
|
||||
|
||||
def flip_and_rotate_image(image_pil):
|
||||
try:
|
||||
cropped_image_pil = crop_by_boxline(image_pil, crop_percent=0.05)
|
||||
mirror_image_pil = mirror_by_image(cropped_image_pil)
|
||||
rotated_image_pil = rotate_by_angle(mirror_image_pil, angle=7, color="white")
|
||||
return rotated_image_pil
|
||||
except Exception as e:
|
||||
logger.error(f"flip_and_rotate_image 처리 중 에러 발생 : {e}")
|
||||
return image_pil
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -151,8 +151,18 @@ def parse_naver_shopping(keyword_id, keyword, isBranch, branchCount, json_data,
|
|||
final_top_5_products = top5_products[:5]
|
||||
logger.debug(f"RANK 정렬상품 중 마지막으로 상위 제품 [{len(final_top_5_products)}]개 선택")
|
||||
|
||||
|
||||
merged_price = 0
|
||||
|
||||
for index, product in enumerate(final_top_5_products):
|
||||
price = int(product.get("item", {}).get("price"))
|
||||
merged_price += price # 평균 가격 계산을 위해
|
||||
|
||||
merged_price = merged_price / len(final_top_5_products)
|
||||
|
||||
c = conn.cursor()
|
||||
|
||||
|
||||
# original relatedTags 리스트 가져오기
|
||||
related_tags_ori = next_data_json["props"]["pageProps"]["relatedTags"]
|
||||
|
||||
|
|
@ -245,8 +255,8 @@ def parse_naver_shopping(keyword_id, keyword, isBranch, branchCount, json_data,
|
|||
|
||||
if count == 0:
|
||||
# 중복되는 데이터가 없으면 새로운 데이터 삽입
|
||||
c.execute("INSERT INTO NaverShopping (keyword_id, keyword, price, productTitle, category1Name, category2Name, category3Name, category4Name, cat_code, openDate, mallCount, keepCnt, overseaTp, reviewCount, reviewCountSum, scoreInfo, naverPayAdAccumulatedDisplayValue, mobileLowPrice, lowPrice, deliveryFeeContent, dlvryLowPrice, imageUrl, imgSz, searchKeyword, mallProductUrl, mallPcUrl, mallName, manuTag, purchaseCnt, relatedTags, rank, unique_id, date_created, time_created) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
(keyword_id+1, current_keyword, price, productTitle, category1Name, category2Name, category3Name, category4Name, cat_code, openDate, mallCount, keepCnt, overseaTp, reviewCount, reviewCountSum, scoreInfo, naverPayAdAccumulatedDisplayValue, mobileLowPrice, lowPrice, deliveryFeeContent, dlvryLowPrice, imageUrl, imgSz, searchKeyword, mallProductUrl, mallPcUrl, mallName, manuTag, purchaseCnt, current_keyword, rank, unique_id, date_created, time_created)) # keyword_id, item_name, price, purchase_count, related_keywords, rank는 적절하게 설정해야 합니다.
|
||||
c.execute("INSERT INTO NaverShopping (keyword_id, keyword, price, productTitle, category1Name, category2Name, category3Name, category4Name, cat_code, openDate, mallCount, keepCnt, overseaTp, reviewCount, reviewCountSum, scoreInfo, naverPayAdAccumulatedDisplayValue, mobileLowPrice, lowPrice, deliveryFeeContent, dlvryLowPrice, imageUrl, imgSz, searchKeyword, mallProductUrl, mallPcUrl, mallName, manuTag, purchaseCnt, relatedTags, rank, merged_price, unique_id, date_created, time_created) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
(keyword_id+1, current_keyword, price, productTitle, category1Name, category2Name, category3Name, category4Name, cat_code, openDate, mallCount, keepCnt, overseaTp, reviewCount, reviewCountSum, scoreInfo, naverPayAdAccumulatedDisplayValue, mobileLowPrice, lowPrice, deliveryFeeContent, dlvryLowPrice, imageUrl, imgSz, searchKeyword, mallProductUrl, mallPcUrl, mallName, manuTag, purchaseCnt, current_keyword, rank, merged_price, unique_id, date_created, time_created)) # keyword_id, item_name, price, purchase_count, related_keywords, rank는 적절하게 설정해야 합니다.
|
||||
logger.debug(f"{current_keyword} DB업데이트 완료")
|
||||
else:
|
||||
# 중복되는 데이터가 있으면 업데이트 또는 무시 (여기서는 예시로 업데이트 로직을 추가함)
|
||||
|
|
@ -259,4 +269,5 @@ def parse_naver_shopping(keyword_id, keyword, isBranch, branchCount, json_data,
|
|||
logger.debug(f"키워드 검색 결과 상품 [{keyword}]에 대한 [{len(final_top_5_products)}]개의 상품정보수집 완료")
|
||||
|
||||
conn.commit() # Commit the transaction
|
||||
|
||||
#conn.close() # Close the connection
|
||||
|
|
|
|||
52
outline10.ui
52
outline10.ui
|
|
@ -48,6 +48,33 @@
|
|||
<height>451</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerItem</enum>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderShowSortIndicator" stdset="0">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="overPrice">
|
||||
<property name="geometry">
|
||||
|
|
@ -133,7 +160,7 @@
|
|||
열기</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="export_btn">
|
||||
<widget class="QPushButton" name="export_btn_by_branch">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>360</x>
|
||||
|
|
@ -287,7 +314,7 @@ XLS저장</string>
|
|||
<string>Save Image</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="export_btn_by_auto">
|
||||
<widget class="QPushButton" name="export_btn_by_main">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>360</x>
|
||||
|
|
@ -337,6 +364,27 @@ font: 75 10pt "Cafe24"</string>
|
|||
<height>231</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>굴림</family>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Box</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||
</property>
|
||||
<property name="autoFormatting">
|
||||
<set>QTextEdit::AutoAll</set>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
|
|
|
|||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
|
|
@ -32,6 +32,6 @@ class TaoScrapingThread(QThread):
|
|||
|
||||
logger.debug("Automatch 작업 완료")
|
||||
except Exception as e:
|
||||
logger.error(f"Automatch 스레드에서 오류 발생: {e}")
|
||||
logger.error(f"Automatch 스레드에서 오류 발생: {e}", exc_info=True)
|
||||
finally:
|
||||
self.finished.emit()
|
||||
778
taoseller.py
778
taoseller.py
File diff suppressed because it is too large
Load Diff
BIN
temp_image.jpg
BIN
temp_image.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 130 KiB |
Loading…
Reference in New Issue