Merge pull request #134 from R0m1k3/claude/add-product-monitoring-01DyNDGnKBX4fHNSjAkwVckd

feat: Add direct Amazon price extraction with CSS selectors
This commit is contained in:
LogiFlow authored and GitHub committed 2025-11-30 00:23:28 +01:00
commit 6ecf315380
2 files changed
+68 -1

No files matched your search

+7 -1
View File
@@ -133,13 +133,19 @@ class AIExtractionMetadata(BaseModel):
EXTRACTION_PROMPT_TEMPLATE = """Extract product price and stock status from this French e-commerce page.
**PRICE (IMPORTANT - French format):**
- **FIRST**: Look for "PRIX DÉTECTÉ:" at the start of text - this is the extracted price
- French prices use COMMA for decimals: "12,99 €" means 12.99
- Thousands separator is SPACE or DOT: "1 234,56 €" or "1.234,56 €" means 1234.56
- Currency symbol is usually € at the end
- Look for: price tags, "Prix:", "€", numbers near "Ajouter au panier"
- Look for: "PRIX DÉTECTÉ:", price tags, "Prix:", "€", numbers near "Ajouter au panier"
- Extract as DECIMAL NUMBER (convert comma to dot): 12,99 -> 12.99
- Ignore crossed-out/barré prices (old prices)
- If multiple prices, take the current/main price (not the original)
- Examples of valid prices:
* "89,99 €" -> 89.99
* "1 234,56 €" -> 1234.56
* "PRIX DÉTECTÉ: 89,99 €" -> 89.99
- If you find ANY price with € symbol, extract it with confidence >= 0.8
- If unclear: set null and confidence < 0.5
**STOCK:**
+61
View File
@@ -142,6 +142,58 @@ class BrowserlessService:
except Exception:
pass
async def _extract_amazon_price(self, page: Page) -> str:
"""
Extract price directly from Amazon product page using CSS selectors.
Returns price text (e.g., "89,99 €") or empty string if not found.
"""
# Amazon price selectors in priority order
# Note: .a-offscreen elements are hidden but contain the full price for screen readers
price_selectors = [
".a-price .a-offscreen", # Most reliable - hidden text with full price
"#corePrice_desktop .a-price .a-offscreen",
"#corePriceDisplay_desktop_feature_div .a-price .a-offscreen",
".a-price[data-a-color='price'] .a-offscreen",
"#priceblock_ourprice", # Older Amazon layout
"#priceblock_dealprice", # Deal prices
"span.a-price-whole", # Visible whole number part
]
for selector in price_selectors:
try:
# Don't check visibility for .a-offscreen elements (they're hidden by design)
element = page.locator(selector).first
# For offscreen elements, just check if they exist and have text
if "offscreen" in selector or "priceblock" in selector:
price_text = await element.inner_text(timeout=2000)
else:
# For visible elements, check visibility first
if await element.is_visible(timeout=2000):
price_text = await element.inner_text()
else:
continue
if price_text and price_text.strip():
logger.info(f"Extracted Amazon price via {selector}: {price_text}")
return price_text.strip()
except Exception:
continue
# Try combination: whole + fraction
try:
whole = await page.locator("span.a-price-whole").first.inner_text()
fraction = await page.locator("span.a-price-fraction").first.inner_text()
if whole and fraction:
price_text = f"{whole}{fraction}"
logger.info(f"Extracted Amazon price from whole+fraction: {price_text}")
return price_text
except Exception:
pass
logger.warning("Could not extract Amazon price with any selector")
return ""
async def get_page_content(self, url: str, use_proxy: bool = False, wait_selector: str = None) -> tuple[str, str]:
"""
Fetch page content with full stealth lifecycle.
@@ -248,6 +300,15 @@ class BrowserlessService:
# Fallback to HTML content if inner_text fails
logger.warning(f"Failed to extract inner_text, falling back to HTML: {e}")
content = await page.content()
# For Amazon, extract price directly and prepend to content
# This helps AI by providing explicit price information
if "amazon" in url.lower() and "/dp/" in url:
amazon_price_text = await self._extract_amazon_price(page)
if amazon_price_text:
content = f"PRIX DÉTECTÉ: {amazon_price_text}\n\n{content}"
logger.info(f"Prepended Amazon price to content: {amazon_price_text}")
# Take screenshot if needed for AI analysis
screenshot_path = ""