AutoPercenty3/test/shoppingScapper/lens_parser.py

200 lines
8.4 KiB
Python

import re
class LensParser:
def __init__(self, app, logger):
self.app = app
self.logger = logger
def extract_product_data(self, list_box):
"""
ListBox에서 제품 데이터를 추출합니다.
Args:
list_box: ListBox 객체
Returns:
list: 추출된 제품 정보의 리스트
"""
product_data = []
try:
# ListBox 내부의 모든 ListItem 순회
list_items = list_box.descendants(control_type="ListItem")
for item in list_items:
# 첫 번째 Hyperlink를 찾기
hyperlink = item.child_window(control_type="Hyperlink", found_index=0)
if hyperlink.exists():
title = hyperlink.window_text()
product_info = self.parse_product_info(title)
if product_info:
product_data.append(product_info)
except Exception as e:
print(f"데이터 추출 중 오류 발생: {e}")
return product_data
# @staticmethod
def parse_product_info_ori(self, titles):
"""
쇼핑 렌즈 결과에서 상품 정보를 파싱합니다.
:param titles: Hyperlink title의 리스트
:return: 구조화된 상품 정보 리스트
"""
products = []
for title in titles:
try:
# 정규식으로 상품명, 가격, 배송비, 판매처 정보 추출
match = re.search(
r"(?P<name>.+?)\s(?P<price>최저\s[0-9,]+원|[0-9,]+원)\s(?P<shipping>무료|배송비\s무료|배송비\s[0-9,]+원)(?:\s판매처\s(?P<seller_count>\d+))?(?:\s별점\s(?P<rating>[0-9.]+))?",
title
)
if match:
product_info = {
"name": match.group("name"),
"price": match.group("price"),
"shipping": match.group("shipping"),
"seller_count": match.group("seller_count") or "1",
"rating": match.group("rating") or None,
}
# 필터링된 상품명에서 중복 제거
filtered_name = self._filter_duplicate_name(product_info["name"])
product_info["filtered_name"] = filtered_name
products.append(product_info)
print(f"product_info : {product_info}")
else:
print(f"상품 정보를 파싱할 수 없습니다: {title}")
except Exception as e:
print(f"상품 정보 파싱 중 오류 발생: {title} -> {e}")
return products
def get_listbox_type(self, listbox_texts):
"""
ListBox 텍스트 내용을 기반으로 타입을 판별합니다.
:param listbox_texts: ListBox 텍스트 리스트
:return: 타입 (1 또는 2)
"""
if any("이 정보가 표시된 이유" in text for text in listbox_texts):
return 1
elif any("북마크 관리자로 가기" in text for text in listbox_texts):
return 2
return None
def parse_product_info(self, list_boxes, whale_window):
"""
쇼핑 렌즈 결과에서 상품 정보를 파싱합니다.
:param titles: Hyperlink title의 리스트
:param whale_window: Whale 브라우저 창 객체
:return: 구조화된 상품 정보 리스트
"""
products = []
try:
# 모든 ListBox - '' 검색
# list_boxes = whale_window.descendants(control_type="List")
for list_box in list_boxes:
listbox_texts = list_box.texts()
listbox_type = self.get_listbox_type(listbox_texts)
print(f"타입 : {listbox_type}")
if listbox_type == 1:
print("1번 타입 ListBox 감지됨, 첫 번째 ListBox에서 검색")
# 1번 타입 처리
for title in listbox_texts:
try:
# 기존의 1번 타입 처리 방식 유지
match = re.search(
r"(?P<name>.+?)\s(?P<price>최저\s[0-9,]+원|[0-9,]+원)\s(?P<shipping>무료|배송비\s무료|배송비\s[0-9,]+원)(?:\s판매처\s(?P<seller_count>\d+))?(?:\s별점\s(?P<rating>[0-9.]+))?",
title
)
if match:
product_info = {
"type": "type1",
"name": match.group("name"),
"price": match.group("price"),
"shipping": match.group("shipping"),
"seller_count": match.group("seller_count") or "1",
"rating": match.group("rating") or None,
}
products.append(product_info)
else:
print(f"1번 타입 - 상품 정보를 파싱할 수 없습니다: {title}")
except Exception as e:
print(f"1번 타입 - 상품 정보 파싱 중 오류 발생: {title} -> {e}")
elif listbox_type == 2:
print("2번 타입 ListBox 감지됨, 두 번째 ListBox에서 검색")
# 2번 타입 처리
list_items = list_box.descendants(control_type="ListItem")
for item in list_items:
try:
# Image 요소에서 제목 추출
image_elem = item.child_window(control_type="Image")
title = image_elem.window_text()
# Text 요소에서 가격 추출
text_elements = item.descendants(control_type="Text")
if len(text_elements) > 1:
price_text = text_elements[1].window_text()
price = int(re.sub(r"[^\d]", "", price_text))
product_info = {
"type": "type2",
"title": title,
"price": price,
}
products.append(product_info)
else:
print(f"2번 타입 - 가격 정보를 찾을 수 없습니다: {item}")
except Exception as e:
print(f"2번 타입 - 상품 정보 파싱 중 오류 발생: {item} -> {e}")
else:
print("알 수 없는 타입의 ListBox입니다. 스킵합니다.")
continue
except Exception as e:
print(f"ListBox 검색 및 파싱 중 오류 발생: {e}")
return products
@staticmethod
def _filter_duplicate_name(name):
"""
상품명에서 중복된 내용을 제거합니다.
:param name: 원본 상품명
:return: 중복 제거된 상품명
"""
words = name.split()
seen = set()
filtered_words = []
for word in words:
if word not in seen:
filtered_words.append(word)
seen.add(word)
return " ".join(filtered_words)
def filter_titles(self, titles):
"""
주어진 제목 리스트에서 '다른 사이트 더보기' 이후 '더보기' 이전의 데이터를 필터링합니다.
Args:
titles (list): 전체 제목 리스트
Returns:
list: 필터링된 제목 리스트
"""
try:
# 시작과 끝 인덱스를 찾기
start_index = titles.index("다른 사이트 더보기") + 1
end_index = titles.index("더보기")
# 필터링된 데이터 추출
filtered_titles = titles[start_index:end_index]
return filtered_titles
except ValueError as e:
# '다른 사이트 더보기' 또는 '더보기'가 없을 경우 예외 처리
print(f"필터링 중 오류 발생: {e}")
return []