""" Base scraper class with common functionality """ import logging from abc import ABC, abstractmethod from dataclasses import dataclass from typing import List, Optional logger = logging.getLogger(__name__) @dataclass class Product: """Represents a product listing""" name: str url: str price: Optional[str] in_stock: bool image_url: Optional[str] = None site: str = "" product_id: Optional[str] = None # Unique identifier for tracking def __hash__(self): return hash(self.url) def __eq__(self, other): if isinstance(other, Product): return self.url == other.url return False class BaseScraper(ABC): """Base class for site-specific scrapers""" site_name: str = "unknown" @abstractmethod def scrape_category_page(self, url: str) -> List[Product]: """ Scrape a category/search page and return all products found Args: url: URL of the category page Returns: List of Product objects """ pass @abstractmethod def check_product_stock(self, product_url: str) -> tuple[bool, Optional[str]]: """ Check if a specific product is in stock Args: product_url: URL of the product page Returns: Tuple of (is_in_stock, price) """ pass def filter_by_keywords(self, products: List[Product], keywords: List[str]) -> List[Product]: """Filter products by keywords in name""" if not keywords: return products filtered = [] for product in products: name_lower = product.name.lower() if any(kw.lower() in name_lower for kw in keywords): filtered.append(product) return filtered