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.+?)\s(?P최저\s[0-9,]+원|[0-9,]+원)\s(?P무료|배송비\s무료|배송비\s[0-9,]+원)(?:\s판매처\s(?P\d+))?(?:\s별점\s(?P[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.+?)\s(?P최저\s[0-9,]+원|[0-9,]+원)\s(?P무료|배송비\s무료|배송비\s[0-9,]+원)(?:\s판매처\s(?P\d+))?(?:\s별점\s(?P[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 []