mirror of
https://github.com/R0m1k3/Priceflow.git
synced 2026-10-23 15:18:12 +02:00
fix: Improve price extraction to avoid fantasy prices on Dashboard
- Added more price selectors (data-testid, price-current, prix-actuel, etc.) - Added validation to reject unreasonable prices: * Reject if <= 0 or < 0.01€ (errors) * Reject if > 100,000€ (wrong element) - Better strikethrough detection to skip old prices - More detailed logging with price values Fixes Dashboard showing incorrect/fantasy prices
This commit is contained in:
1 file changed
+38
-10
@@ -146,28 +146,39 @@ class BrowserlessService:
|
|||||||
"""
|
"""
|
||||||
Extract price from generic e-commerce pages using common selectors and patterns.
|
Extract price from generic e-commerce pages using common selectors and patterns.
|
||||||
Returns price text (e.g., "1,99 €") or empty string if not found.
|
Returns price text (e.g., "1,99 €") or empty string if not found.
|
||||||
|
IMPROVED: Better validation to avoid "fantasy" prices
|
||||||
"""
|
"""
|
||||||
# Common price selectors used across e-commerce sites (ordered by priority)
|
# Common price selectors used across e-commerce sites (ordered by priority)
|
||||||
price_selectors = [
|
price_selectors = [
|
||||||
# Highest priority: semantic and explicit current prices
|
# Highest priority: semantic and explicit current prices
|
||||||
"[itemprop='price']",
|
"[itemprop='price']",
|
||||||
|
"[data-testid='price']",
|
||||||
|
"[data-test='price']",
|
||||||
".current-price",
|
".current-price",
|
||||||
".sale-price",
|
".sale-price",
|
||||||
".final-price",
|
".final-price",
|
||||||
".product-price",
|
".product-price",
|
||||||
|
".special-price",
|
||||||
"[data-price]",
|
"[data-price]",
|
||||||
# Medium priority: generic price classes (exclude old/was/original)
|
# Specific e-commerce platforms
|
||||||
"[class*='price']:not([class*='old']):not([class*='was']):not([class*='original']):not([class*='before']):not([class*='regular'])",
|
".price-current",
|
||||||
|
".price-now",
|
||||||
|
".price-sales",
|
||||||
|
# Medium priority: generic price classes (exclude old/was/original/strikethrough)
|
||||||
|
"[class*='price']:not([class*='old']):not([class*='was']):not([class*='original']):not([class*='before']):not([class*='regular']):not([class*='strike']):not([class*='barre'])",
|
||||||
# ID-based
|
# ID-based
|
||||||
"#price",
|
"#price",
|
||||||
"#product-price",
|
"#product-price",
|
||||||
"#our-price",
|
"#our-price",
|
||||||
|
"#main-price",
|
||||||
# French-specific
|
# French-specific
|
||||||
"span[class*='prix']:not([class*='ancien']):not([class*='barre'])",
|
"span[class*='prix']:not([class*='ancien']):not([class*='barre']):not([class*='promotion'])",
|
||||||
"div[class*='prix']:not([class*='ancien'])",
|
"div[class*='prix']:not([class*='ancien']):not([class*='barre'])",
|
||||||
"span[class*='tarif']",
|
"span[class*='tarif']",
|
||||||
|
".prix-actuel",
|
||||||
|
".prix-vente",
|
||||||
# Lower priority: generic .price (might catch old prices)
|
# Lower priority: generic .price (might catch old prices)
|
||||||
".price",
|
".price:not(.old-price):not(.was-price)",
|
||||||
]
|
]
|
||||||
|
|
||||||
found_prices = []
|
found_prices = []
|
||||||
@@ -196,12 +207,29 @@ class BrowserlessService:
|
|||||||
if price_text and ('€' in price_text or (',' in price_text and any(c.isdigit() for c in price_text))):
|
if price_text and ('€' in price_text or (',' in price_text and any(c.isdigit() for c in price_text))):
|
||||||
# Clean up price text
|
# Clean up price text
|
||||||
price_text = price_text.strip()
|
price_text = price_text.strip()
|
||||||
found_prices.append((selector, price_text))
|
|
||||||
logger.info(f"Found price via selector {selector}: {price_text}")
|
|
||||||
|
|
||||||
# Return immediately if we found with high-priority selector
|
# VALIDATION: Check if price is reasonable (not fantasy)
|
||||||
if selector in ["[itemprop='price']", ".current-price", ".sale-price", ".final-price", ".product-price"]:
|
import re
|
||||||
return price_text
|
numeric_match = re.search(r'(\d+[.,]?\d*)', price_text.replace(' ', '').replace('\xa0', ''))
|
||||||
|
if numeric_match:
|
||||||
|
try:
|
||||||
|
# Parse as float
|
||||||
|
price_val = float(numeric_match.group(1).replace(',', '.'))
|
||||||
|
|
||||||
|
# Reject unreasonable prices
|
||||||
|
if price_val <= 0 or price_val < 0.01 or price_val > 100000:
|
||||||
|
logger.debug(f"Rejected unreasonable price: {price_val}€ from {selector}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
found_prices.append((selector, price_text))
|
||||||
|
logger.info(f"Found valid price via {selector}: {price_text} ({price_val}€)")
|
||||||
|
|
||||||
|
# Return immediately if high-priority
|
||||||
|
if selector in ["[itemprop='price']", "[data-testid='price']", ".current-price", ".sale-price", ".final-price", ".product-price"]:
|
||||||
|
return price_text
|
||||||
|
except (ValueError, AttributeError):
|
||||||
|
logger.debug(f"Could not parse price: {price_text}")
|
||||||
|
continue
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
Reference in new issue
Block a user