2nd commit
This commit is contained in:
parent
266de84acf
commit
e2a33ebbb7
|
|
@ -0,0 +1,12 @@
|
|||
[Naver_API]
|
||||
client_id = Uq5c9J_WdQYF8e2wOQT4
|
||||
client_secret = y0CnrADAae
|
||||
; client_id = 'HBcquewobashdSyPtBbE'
|
||||
; client_secret = 't3tYyWYj1e'
|
||||
[GPT_API]
|
||||
api_key = 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
|
||||
|
||||
[Rapid_API]
|
||||
url = https://taobao-datahub.p.rapidapi.com/item_search_image_2
|
||||
x-rapidapi-key = cadd2a7246msh0dc206858f6e495p105151jsn7bdf01e8aa3f
|
||||
x-rapidapi-host = taobao-datahub.p.rapidapi.com
|
||||
BIN
data/app.db
BIN
data/app.db
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -16,12 +16,16 @@ class DBManager:
|
|||
CREATE TABLE IF NOT EXISTS products (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT,
|
||||
generatedTitle TEXT,
|
||||
image_url TEXT,
|
||||
price INTEGER,
|
||||
Taoid TEXT,
|
||||
TaoTitle TEXT,
|
||||
Tao_URL TEXT,
|
||||
Tao_image_url TEXT,
|
||||
Tao_price INTEGER,
|
||||
sold_price INTEGER,
|
||||
translatedTitle TEXT,
|
||||
tags TEXT,
|
||||
category_code TEXT,
|
||||
memo TEXT,
|
||||
|
|
@ -52,14 +56,25 @@ class DBManager:
|
|||
try:
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
UPDATE products
|
||||
SET Taoid = ?, Tao_URL = ?, Tao_image_url = ?, Tao_price = ?
|
||||
WHERE id = ?
|
||||
""", (
|
||||
item.get("Taoid"), item.get("Tao_URL"), item.get("Tao_image_url"),
|
||||
item.get("Tao_price"),item.get("id")
|
||||
))
|
||||
# 업데이트할 필드 구성
|
||||
fields_to_update = {
|
||||
"Taoid": item.get("Taoid"),
|
||||
"Tao_URL": item.get("Tao_URL"),
|
||||
"Tao_image_url": item.get("Tao_image_url"),
|
||||
"Tao_price": item.get("Tao_price"),
|
||||
"TaoTitle": item.get("TaoTitle"),
|
||||
"translatedTitle": item.get("translatedTitle"),
|
||||
"generatedTitle": item.get("generatedTitle"),
|
||||
"sold_price": item.get("sold_price")
|
||||
}
|
||||
|
||||
# 유효한 필드만 업데이트 SQL에 추가
|
||||
set_clause = ", ".join(f"{key} = ?" for key in fields_to_update if fields_to_update[key] is not None)
|
||||
values = [fields_to_update[key] for key in fields_to_update if fields_to_update[key] is not None]
|
||||
values.append(item["id"]) # WHERE 조건에 사용
|
||||
|
||||
sql = f"UPDATE products SET {set_clause} WHERE id = ?"
|
||||
cursor.execute(sql, values)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
self.logger.log(f"DB 데이터 업데이트 완료: ID={item['id']}", level=logging.DEBUG)
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@ class ExcelExporter:
|
|||
row_num = 4 + (index % 50)
|
||||
self.logger.log(f"{index + 1}번째 행 기록 시작: B{row_num}, C{row_num}, D{row_num}, F{row_num}, G{row_num}, H{row_num}", level=logging.DEBUG) # 셀 위치 로그 추가
|
||||
ws.range(f'B{row_num}').value = row['Tao_URL']
|
||||
ws.range(f'C{row_num}').value = row['name']
|
||||
ws.range(f'D{row_num}').value = row['price']
|
||||
ws.range(f'C{row_num}').value = row['generatedTitle']
|
||||
ws.range(f'D{row_num}').value = row['sold_price']
|
||||
ws.range(f'F{row_num}').value = row['tags']
|
||||
ws.range(f'G{row_num}').value = row['category_code']
|
||||
ws.range(f'H{row_num}').value = row['memo']
|
||||
|
|
|
|||
|
|
@ -11,12 +11,6 @@ class GPTClient:
|
|||
self.model = model
|
||||
self.temperature = temperature
|
||||
|
||||
|
||||
client_id='Uq5c9J_WdQYF8e2wOQT4'
|
||||
client_secret='y0CnrADAae'
|
||||
|
||||
self.gpt = GPTClient(self.logger, api_key='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')
|
||||
|
||||
def ask(self, prompt: str) -> str:
|
||||
"""프롬프트를 이용하여 GPT 모델로부터 응답을 받습니다."""
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -56,6 +56,19 @@ class KeywordManager(QDialog):
|
|||
else:
|
||||
QMessageBox.warning(self, "추가 실패", "금지어를 입력해주세요!")
|
||||
|
||||
def get_BAN_List(self):
|
||||
"""
|
||||
금지어 리스트를 반환합니다.
|
||||
:return: 금지어 리스트
|
||||
"""
|
||||
try:
|
||||
keywords = [self.keyword_list.item(i).text().strip() for i in range(self.keyword_list.count())]
|
||||
self.logger.log(f"금지어 리스트 반환: {keywords}", level=logging.DEBUG)
|
||||
return keywords
|
||||
except Exception as e:
|
||||
self.logger.log(f"금지어 리스트 반환 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
||||
return []
|
||||
|
||||
def delete_keyword(self):
|
||||
selected_items = self.keyword_list.selectedItems()
|
||||
if selected_items:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
from pywinauto import Application, findwindows, timings
|
||||
from pywinauto.timings import wait_until
|
||||
from pywinauto import Application, findwindows, timings
|
||||
from pywinauto.controls.hwndwrapper import HwndWrapper
|
||||
from typing import Dict, List
|
||||
import os, sys
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import requests
|
||||
|
||||
import logging
|
||||
class NaverSearchAPI:
|
||||
def __init__(self, client_id: str, client_secret: str, logger=None):
|
||||
"""
|
||||
|
|
@ -12,13 +12,6 @@ class NaverSearchAPI:
|
|||
self.client_secret = client_secret
|
||||
self.logger = logger
|
||||
|
||||
|
||||
client_id='Uq5c9J_WdQYF8e2wOQT4'
|
||||
client_secret='y0CnrADAae'
|
||||
|
||||
self.gpt = GPTClient(self.logger, api_key='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')
|
||||
|
||||
|
||||
def search(self, query: str, display: int = 10, start: int = 1, sort: str = "sim") -> dict:
|
||||
"""
|
||||
네이버 검색 API로 검색어를 검색
|
||||
|
|
@ -40,31 +33,34 @@ class NaverSearchAPI:
|
|||
"sort": sort
|
||||
}
|
||||
|
||||
print(f"self.client_id : {self.client_id}")
|
||||
print(f"self.client_secret : {self.client_secret}")
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=headers, params=params)
|
||||
response.raise_for_status() # HTTP 에러 발생 시 예외 처리
|
||||
data = response.json()
|
||||
|
||||
if self.logger:
|
||||
self.logger.info(f"네이버 검색 성공: {query}, 결과 개수: {len(data.get('items', []))}")
|
||||
|
||||
self.logger.log(f"네이버 검색 성공: {query}, 결과 개수: {len(data.get('items', []))}", level=logging.DEBUG)
|
||||
return data
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
if self.logger:
|
||||
self.logger.error(f"네이버 검색 실패: {e}", exc_info=True)
|
||||
self.logger.log(f"네이버 검색 실패: {e}", level=logging.DEBUG, exc_info=True)
|
||||
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
|
||||
n_search = NaverSearchAPI(client_id='Uq5c9J_WdQYF8e2wOQT4', client_secret='y0CnrADAae')
|
||||
# n_search = NaverSearchAPI(client_id='Uq5c9J_WdQYF8e2wOQT4', client_secret='y0CnrADAae')
|
||||
|
||||
query = "섬유 시편 원단 수동 절단기 컷팅기 샘플러 나이프"
|
||||
search_results = n_search.search(query, display=20, start=1, sort="sim")
|
||||
# query = "섬유 시편 원단 수동 절단기 컷팅기 샘플러 나이프"
|
||||
# search_results = n_search.search(query, display=20, start=1, sort="sim")
|
||||
|
||||
if "error" in search_results:
|
||||
print("검색 실패:", search_results["error"])
|
||||
else:
|
||||
for item in search_results.get("items", []):
|
||||
print(f"{item}")
|
||||
# print(f"상품명: {item['title']}, 가격: {item['lprice']}, 링크: {item['link']}")
|
||||
# if "error" in search_results:
|
||||
# print("검색 실패:", search_results["error"])
|
||||
# else:
|
||||
# for item in search_results.get("items", []):
|
||||
# print(f"{item}")
|
||||
# # print(f"상품명: {item['title']}, 가격: {item['lprice']}, 링크: {item['link']}")
|
||||
|
|
|
|||
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,6 @@
|
|||
Response URL: https://api.scopic.naver.com/whale?msgpad=1734050078279&md=KCQvMCC4%2Fv5NDFRPPWrGKehP3zU%3D
|
||||
Status Code: 200
|
||||
Headers: Headers[(b'date', b'Fri, 13 Dec 2024 00:34:41 GMT'), (b'content-type', b'application/json; charset=utf-8'), (b'content-length', b'1904'), (b'strict-transport-security', b'max-age=31536000; includeSubDomains')]
|
||||
Body: {"id":"aHR0cHM6Ly9zYmlueC1waGluZi5wc3RhdGljLm5ldC9NakF5TkRFeU1UTmZPVElnL01EQXhOek0wTURVd01EZ3dNak0wLmU2UmlkNkpVeVJkYnQyY2plamQxU2pfQ0R0VFlPamJ6M2JzSzIydnVVVklnLldhZE9KdFFieGVjWDFxNVBvX3JtR3podDF5OXdISGttai1KV0FfQ0ZqTndnLlBORy9pbWFnZS5wbmc=","url":"https://m.search.naver.com/search.naver?sm=mob_len&qdt=0&query=%EC%95%85%EA%B8%B0%20%EC%BC%80%EC%9D%B4%EC%8A%A4%20%EA%B0%80%EB%B0%A9%20%ED%95%98%EB%93%9C%20%EC%86%8C%ED%94%84%ED%8A%B8%20%EC%B4%88%EA%B2%BD%EB%9F%89%20%EC%BA%90%EB%A6%AC%EC%96%B4%20%EC%83%89%EC%86%8C%ED%8F%B0%20%EB%B0%B1%ED%8C%A9%20%EC%A4%91%20%EC%88%98%EB%82%A9%20E%EC%8A%A4%ED%94%BC%EC%BB%A4%20%EC%82%AD%EC%8A%A4%20%EB%B0%B1%20%EC%88%84%EB%8D%94%20%EC%8A%A4%ED%80%98%EC%96%B4%20%EB%B0%B4%EB%93%9C%20%EC%9D%B8%ED%83%80%EC%9D%B4%EC%96%B4&x_sbi=%7B%22coord%22%3A%22x%3A15%2Cy%3A108%2Cw%3A179%2Ch%3A378%22%2C%22from%22%3A%22nx_shpcamera%22%2C%22query%22%3A%22%EC%95%85%EA%B8%B0%20%EC%BC%80%EC%9D%B4%EC%8A%A4%20%EA%B0%80%EB%B0%A9%20%ED%95%98%EB%93%9C%20%EC%86%8C%ED%94%84%ED%8A%B8%20%EC%B4%88%EA%B2%BD%EB%9F%89%20%EC%BA%90%EB%A6%AC%EC%96%B4%20%EC%83%89%EC%86%8C%ED%8F%B0%20%EB%B0%B1%ED%8C%A9%20%EC%A4%91%20%EC%88%98%EB%82%A9%20E%EC%8A%A4%ED%94%BC%EC%BB%A4%20%EC%82%AD%EC%8A%A4%20%EB%B0%B1%20%EC%88%84%EB%8D%94%20%EC%8A%A4%ED%80%98%EC%96%B4%20%EB%B0%B4%EB%93%9C%20%EC%9D%B8%ED%83%80%EC%9D%B4%EC%96%B4%22%2C%22querytype%22%3A%22shopping%22%2C%22sbiid%22%3A%22aHR0cHM6Ly9zYmlueC1waGluZi5wc3RhdGljLm5ldC9NakF5TkRFeU1UTmZPVElnL01EQXhOek0wTURVd01EZ3dNak0wLmU2UmlkNkpVeVJkYnQyY2plamQxU2pfQ0R0VFlPamJ6M2JzSzIydnVVVklnLldhZE9KdFFieGVjWDFxNVBvX3JtR3podDF5OXdISGttai1KV0FfQ0ZqTndnLlBORy9pbWFnZS5wbmc%3D%22%7D&x_image=%7B%22NDI%22%3A1%7D&mra=QkRS%5ETE5F%5EU1BI%5EUkxF%5EVWJHX0xSbQ%3D%3D","imageUrl":"https://sbinx-phinf.pstatic.net/MjAyNDEyMTNfOTIg/MDAxNzM0MDUwMDgwMjM0.e6Rid6JUyRdbt2cjejd1Sj_CDtTYOjbz3bsK22vuUVIg.WadOJtQbxecX1q5Po_rmGzht1y9wHHkmj-JWA_CFjNwg.PNG/image.png","target":"real","status":"ok"}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
from mitmproxy import http
|
||||
import os
|
||||
|
||||
OUTPUT_DIR = "captured_data"
|
||||
|
||||
# 디렉토리 생성
|
||||
if not os.path.exists(OUTPUT_DIR):
|
||||
os.makedirs(OUTPUT_DIR)
|
||||
|
||||
def request(flow: http.HTTPFlow):
|
||||
# 요청 URL 저장
|
||||
with open(os.path.join(OUTPUT_DIR, "requests.txt"), "a", encoding="utf-8") as f:
|
||||
f.write(f"Request URL: {flow.request.url}\n")
|
||||
f.write(f"Headers: {flow.request.headers}\n")
|
||||
f.write(f"Body: {flow.request.content}\n")
|
||||
f.write("\n")
|
||||
|
||||
def response(flow: http.HTTPFlow):
|
||||
# 특정 URL에 대한 응답 데이터 저장
|
||||
if "api.scopic.naver.com" in flow.request.url:
|
||||
with open(os.path.join(OUTPUT_DIR, "responses.txt"), "a", encoding="utf-8") as f:
|
||||
f.write(f"Response URL: {flow.request.url}\n")
|
||||
f.write(f"Status Code: {flow.response.status_code}\n")
|
||||
f.write(f"Headers: {flow.response.headers}\n")
|
||||
f.write(f"Body: {flow.response.text}\n")
|
||||
f.write("\n")
|
||||
|
|
@ -4,6 +4,9 @@ from PySide6.QtWidgets import (
|
|||
QPushButton, QTableWidget, QTableWidgetItem, QTextEdit, QMessageBox, QCheckBox
|
||||
)
|
||||
from PySide6.QtCore import Qt
|
||||
import configparser
|
||||
import math
|
||||
from deep_translator import GoogleTranslator
|
||||
|
||||
from src.export_xls import ExcelExporter
|
||||
from src.db_manager import DBManager
|
||||
|
|
@ -12,12 +15,24 @@ import pandas as pd
|
|||
from src.keyword_manager import KeywordManager
|
||||
from src.categoryManager import CategoryManager
|
||||
from src.tables import CustomTableWidget
|
||||
from src.naverAPI import NaverSearchAPI
|
||||
from src.gpt_Client import GPTClient
|
||||
import requests
|
||||
|
||||
class UIManager(QMainWindow):
|
||||
def __init__(self, logger):
|
||||
super().__init__()
|
||||
self.logger = logger
|
||||
|
||||
# Config 파일 읽기
|
||||
self.config = self.load_config("config.ini")
|
||||
self.naver_client_id = self.config.get("Naver_API", "client_id")
|
||||
self.naver_client_secret = self.config.get("Naver_API", "client_secret")
|
||||
self.gpt_api_key = self.config.get("GPT_API", "api_key")
|
||||
self.rapid_api_url = self.config.get("Rapid_API", "url")
|
||||
self.rapid_api_key = self.config.get("Rapid_API", "x-rapidapi-key")
|
||||
self.rapid_api_host = self.config.get("Rapid_API", "x-rapidapi-host")
|
||||
|
||||
self.logger.log("UI Manager 초기화 중...", level=logging.DEBUG)
|
||||
|
||||
# 윈도우 설정
|
||||
|
|
@ -31,6 +46,10 @@ class UIManager(QMainWindow):
|
|||
baseXLS_path = os.path.join("resources", "baseXLS_Percenty.xlsx")
|
||||
self.excel_exporter = ExcelExporter(self.logger, self.db_manager, base_excel_path=baseXLS_path)
|
||||
self.category_manager = CategoryManager(logger=self.logger, excel_path=baseXLS_path)
|
||||
self.naverAPI = NaverSearchAPI(client_id=self.naver_client_id, client_secret=self.naver_client_secret, logger=self.logger)
|
||||
self.gpt = GPTClient(logger=self.logger, api_key=self.gpt_api_key)
|
||||
self.keyword_manager = KeywordManager(self.logger, self)
|
||||
self.gtranslator = GoogleTranslator(source="zh-CN", target="ko")
|
||||
|
||||
# 메인 레이아웃 설정
|
||||
self.central_widget = QWidget()
|
||||
|
|
@ -41,6 +60,7 @@ class UIManager(QMainWindow):
|
|||
# 버튼 설정
|
||||
self.folder_button = QPushButton("폴더 선택")
|
||||
self.api_button = QPushButton("API 호출")
|
||||
self.post_button = QPushButton("후처리")
|
||||
self.export_button = QPushButton("엑셀 출력")
|
||||
self.keyword_button = QPushButton("금지어 관리")
|
||||
|
||||
|
|
@ -61,6 +81,7 @@ class UIManager(QMainWindow):
|
|||
button_layout = QHBoxLayout()
|
||||
button_layout.addWidget(self.folder_button)
|
||||
button_layout.addWidget(self.api_button)
|
||||
button_layout.addWidget(self.post_button)
|
||||
button_layout.addWidget(self.export_button)
|
||||
button_layout.addWidget(self.keyword_button)
|
||||
|
||||
|
|
@ -72,6 +93,7 @@ class UIManager(QMainWindow):
|
|||
# 버튼 이벤트 연결
|
||||
self.folder_button.clicked.connect(self.select_folder)
|
||||
self.api_button.clicked.connect(self.call_api)
|
||||
self.post_button.clicked.connect(self.post_data)
|
||||
self.export_button.clicked.connect(self.export_data)
|
||||
self.keyword_button.clicked.connect(self.manage_keywords)
|
||||
|
||||
|
|
@ -106,6 +128,18 @@ class UIManager(QMainWindow):
|
|||
self.logger.log(f"엑셀 파일 처리 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
||||
QMessageBox.critical(self, "폴더 선택", "엑셀 파일 처리 중 오류가 발생했습니다!")
|
||||
|
||||
def load_config(self, file_path: str) -> configparser.ConfigParser:
|
||||
"""
|
||||
config.ini 파일을 읽어서 ConfigParser 객체로 반환
|
||||
"""
|
||||
config = configparser.ConfigParser()
|
||||
try:
|
||||
config.read(file_path)
|
||||
self.logger.log(f"Config 파일 '{file_path}' 로드 성공", level=logging.INFO)
|
||||
except Exception as e:
|
||||
self.logger.log(f"Config 파일 로드 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
||||
return config
|
||||
|
||||
def load_keywords(self):
|
||||
"""
|
||||
금지어 목록을 파일에서 로드합니다.
|
||||
|
|
@ -219,14 +253,13 @@ class UIManager(QMainWindow):
|
|||
product_id = int(self.table.item(row, 1).text()) # ID 값은 1번째 열
|
||||
|
||||
# API 호출
|
||||
url = "https://taobao-datahub.p.rapidapi.com/item_search_image_2"
|
||||
querystring = {"imgUrl": image_url, "pageSize": "10"}
|
||||
headers = {
|
||||
"x-rapidapi-key": "cadd2a7246msh0dc206858f6e495p105151jsn7bdf01e8aa3f", # RapidAPI 키를 입력하세요
|
||||
"x-rapidapi-host": "taobao-datahub.p.rapidapi.com"
|
||||
"x-rapidapi-key": self.rapid_api_key, # RapidAPI 키를 입력하세요
|
||||
"x-rapidapi-host": self.rapid_api_host
|
||||
}
|
||||
|
||||
response = requests.get(url, headers=headers, params=querystring)
|
||||
response = requests.get(self.rapid_api_url, headers=headers, params=querystring)
|
||||
response.raise_for_status()
|
||||
|
||||
# 응답 데이터 파싱
|
||||
|
|
@ -244,6 +277,10 @@ class UIManager(QMainWindow):
|
|||
self.logger.log(f"선택된 상품: {top_item}", level=logging.INFO)
|
||||
self.log_text.append(f"선택된 상품 ID={tao_id}, 가격={top_item['price']}, 이미지 URL={top_item['image']}")
|
||||
|
||||
translatedTitle = self.translate_name(top_item["title"])
|
||||
self.logger.log(f"원본 상품명: {top_item["title"]}", level=logging.DEBUG)
|
||||
self.logger.log(f"번역된 상품: {translatedTitle}", level=logging.DEBUG)
|
||||
|
||||
# 결과를 DB에 저장
|
||||
self.db_manager.update_item({
|
||||
"id": product_id, # ID 추가
|
||||
|
|
@ -251,6 +288,8 @@ class UIManager(QMainWindow):
|
|||
"Tao_URL": tao_url,
|
||||
"Tao_image_url": top_item["image"],
|
||||
"Tao_price": top_item["price"],
|
||||
"TaoTitle": top_item["title"],
|
||||
"translatedTitle": translatedTitle,
|
||||
})
|
||||
# 테이블 업데이트
|
||||
self.table.setItem(row, 5, QTableWidgetItem(f"ID={tao_id}, 가격={top_item['price']}"))
|
||||
|
|
@ -263,6 +302,142 @@ class UIManager(QMainWindow):
|
|||
self.table.setItem(row, 5, QTableWidgetItem("API 호출 실패"))
|
||||
self.log_text.append(f"API 호출 실패: 이미지 URL={image_url}")
|
||||
|
||||
def translate_name(self, text: str) -> str:
|
||||
"""
|
||||
중국어 텍스트를 한국어로 번역하는 메서드
|
||||
:param text: 번역할 텍스트
|
||||
:return: 번역된 한국어 텍스트
|
||||
"""
|
||||
if not text.strip():
|
||||
self.logger.log(f"빈 텍스트가 입력되었습니다.", level=logging.WARNING)
|
||||
return ""
|
||||
|
||||
try:
|
||||
# 번역 수행
|
||||
result = self.gtranslator.translate(text)
|
||||
self.logger.log(f"번역 성공: {text} -> {result}", level=logging.DEBUG)
|
||||
return result
|
||||
except Exception as e:
|
||||
self.logger.log(f"번역 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
||||
|
||||
return "번역 실패"
|
||||
|
||||
def is_valid_word(self, word: str) -> bool:
|
||||
"""숫자로만 이루어진 단어 또는 영어와 숫자로만 이루어진 단어를 검증하는 함수."""
|
||||
return not (word.isdigit() or re.fullmatch(r'[A-Za-z0-9]+', word))
|
||||
|
||||
def extract_special_words(self, original_name: str) -> list:
|
||||
"""원본 상품명에서 숫자로만 이루어진 단어와 영어와 숫자로만 이루어진 단어를 추출하는 함수."""
|
||||
return [word for word in original_name.split() if word.isdigit() or re.fullmatch(r'[A-Za-z0-9]+', word)]
|
||||
|
||||
def filter_invalid_words(self, words: list) -> list:
|
||||
"""영어만 이루어진 단어와 영어와 숫자로 이루어진 단어를 제외하는 함수."""
|
||||
return [word for word in words if not re.fullmatch(r'[A-Za-z0-9]+', word)]
|
||||
|
||||
def clean_text(self, text):
|
||||
"""
|
||||
입력 문자열에서 특수문자를 제거하고 정리된 문자열 반환.
|
||||
"""
|
||||
# 정규식으로 특수문자를 공백으로 치환
|
||||
return re.sub(r"[^\w\s]", " ", text)
|
||||
|
||||
def post_data(self):
|
||||
"""
|
||||
DB에서 데이터를 가져와 NaverSearchAPI를 사용하여 후처리.
|
||||
상품명을 생성하고 판매 가격을 계산하여 DB에 업데이트.
|
||||
"""
|
||||
try:
|
||||
# DB에서 모든 데이터 가져오기
|
||||
data = self.db_manager.fetch_all()
|
||||
|
||||
if data.empty:
|
||||
self.logger.log("DB에서 가져온 데이터가 없습니다.", level=logging.WARNING)
|
||||
return
|
||||
|
||||
for _, row in data.iterrows():
|
||||
item_id = row["id"]
|
||||
query = row["name"]
|
||||
originalTitle = row["TaoTitle"]
|
||||
|
||||
# 검색 키워드 설정: 첫 4개의 단어 추출
|
||||
search_keywords = " ".join(query.split()[:4])
|
||||
self.logger.log(f"검색 실행: {search_keywords} (ID: {item_id})", level=logging.INFO)
|
||||
|
||||
# 네이버 API 호출
|
||||
search_results = self.naverAPI.search(query=search_keywords)
|
||||
items = search_results.get("items", [])
|
||||
|
||||
if not items:
|
||||
self.logger.log(f"검색 결과가 없습니다: {search_keywords}", level=logging.WARNING)
|
||||
continue
|
||||
|
||||
# 상품명 리스트 처리
|
||||
titles = [self.strip_tags(item["title"]) for item in items]
|
||||
|
||||
# 특수문자 제거 및 단어 분리
|
||||
cleaned_titles = [self.clean_text(title) for title in titles]
|
||||
|
||||
# 단어 단위로 분리 후 중복 제거
|
||||
keyword_title = set(" ".join(cleaned_titles).split())
|
||||
|
||||
# 숫자나 영어 또는 숫자로만 이루어진 단어 필터링
|
||||
keyword_title = [word for word in keyword_title if self.is_valid_word(word)]
|
||||
self.logger.log(f"keyword_title after filtering invalid words : {keyword_title}", level=logging.DEBUG)
|
||||
|
||||
# 7. 원본 상품명에서 숫자 또는 영어와 숫자로만 이루어진 단어 추출 및 포함
|
||||
special_words = self.extract_special_words(originalTitle)
|
||||
self.logger.log(f"special_words from original_name: {special_words}", level=logging.DEBUG)
|
||||
keyword_title.extend(special_words)
|
||||
|
||||
# 금지어 필터링
|
||||
ban_keyword = self.keyword_manager.get_BAN_List()
|
||||
self.logger.log(f"ban_keyword: {ban_keyword}", level=logging.DEBUG)
|
||||
filtered_words = [word for word in keyword_title if word not in ban_keyword]
|
||||
|
||||
# GPT를 사용한 상품명 생성
|
||||
generated_name = self.gpt.generate_product_name_next(filtered_words, originalTitle, titles, unique_first_two_words=special_words)
|
||||
self.logger.log(f"생성된 상품명: {generated_name} (ID: {item_id})", level=logging.INFO)
|
||||
|
||||
# 2. 가격 계산
|
||||
prices = [float(item["lprice"]) for item in items if item["lprice"].isdigit()]
|
||||
prices.append(float(row["price"])) # 기존 DB의 가격 포함
|
||||
|
||||
avg_price = sum(prices) / len(prices)
|
||||
median_price = sorted(prices)[len(prices) // 2]
|
||||
final_price = max(avg_price, median_price) # 평균값과 중간값 중 큰 값
|
||||
|
||||
# 1000원 단위로 올림
|
||||
final_price = math.ceil(final_price / 1000) * 1000
|
||||
avg_price = math.ceil(avg_price / 1000) * 1000
|
||||
median_price = math.ceil(median_price / 1000) * 1000
|
||||
|
||||
self.logger.log(f"계산된 판매 가격: {final_price} (ID: {item_id})", level=logging.INFO)
|
||||
|
||||
# DB 업데이트
|
||||
self.db_manager.update_item({
|
||||
"id": item_id,
|
||||
"generatedTitle": generated_name,
|
||||
"sold_price": final_price
|
||||
})
|
||||
|
||||
self.logger.log("후처리 완료.", level=logging.INFO)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.log(f"후처리 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
||||
|
||||
def strip_tags(self, html: str) -> str:
|
||||
"""
|
||||
HTML 태그를 제거하여 순수 텍스트만 반환.
|
||||
"""
|
||||
import re
|
||||
return re.sub(r"<.*?>", "", html)
|
||||
|
||||
def get_banned_words(self):
|
||||
"""
|
||||
금지어 목록 반환 (예: 파일이나 DB에서 불러올 수 있음).
|
||||
"""
|
||||
return ["금지어1", "금지어2"] # 실제 금지어 목록으로 대체
|
||||
|
||||
def export_data(self):
|
||||
success = self.excel_exporter.save_to_excel()
|
||||
if success:
|
||||
|
|
@ -272,8 +447,7 @@ class UIManager(QMainWindow):
|
|||
|
||||
def manage_keywords(self):
|
||||
self.logger.log("금지어 관리 버튼 클릭됨", level=logging.DEBUG)
|
||||
keyword_manager = KeywordManager(self.logger, self)
|
||||
keyword_manager.exec()
|
||||
self.keyword_manager.exec()
|
||||
|
||||
# def populate_table(self):
|
||||
# # 테이블 초기화
|
||||
|
|
@ -352,6 +526,7 @@ class UIManager(QMainWindow):
|
|||
items = []
|
||||
for entry in result_list:
|
||||
item = entry.get("item", {})
|
||||
# title = entry.get("title", {})
|
||||
sales_str = entry.get("item", {}).get("sales", "0").replace("+", "") # "+" 제거
|
||||
sales = self.parse_sales(sales_str) # 판매량 변환
|
||||
|
||||
|
|
@ -367,6 +542,7 @@ class UIManager(QMainWindow):
|
|||
items.append({
|
||||
"itemId": item.get("itemId"),
|
||||
"image": item.get("image"),
|
||||
"title": item.get("title"),
|
||||
"price": price,
|
||||
"promotionPrice": promotion_price,
|
||||
"sales": sales
|
||||
|
|
@ -381,7 +557,8 @@ class UIManager(QMainWindow):
|
|||
return {
|
||||
"itemId": top_item["itemId"],
|
||||
"image": f"https:{top_item['image']}", # URL 앞에 "https:" 추가
|
||||
"price": top_item["price"]
|
||||
"price": top_item["price"],
|
||||
"title": top_item["title"]
|
||||
}
|
||||
|
||||
return None # 유효한 상품이 없으면 None 반환
|
||||
|
|
|
|||
Loading…
Reference in New Issue