mirror of
https://github.com/R0m1k3/Priceflow.git
synced 2026-10-24 07:38:13 +02:00
Merge pull request #120 from R0m1k3/claude/add-product-monitoring-01DyNDGnKBX4fHNSjAkwVckd
feat: Extract visible text and optimize Amazon price detection
This commit is contained in:
2 files changed
+69
-4
No files matched your search
@@ -343,8 +343,14 @@ class AIService:
|
|||||||
if len(cleaned_text) > MAX_TEXT_LENGTH:
|
if len(cleaned_text) > MAX_TEXT_LENGTH:
|
||||||
cleaned_text = cleaned_text[:MAX_TEXT_LENGTH] + "...(truncated)"
|
cleaned_text = cleaned_text[:MAX_TEXT_LENGTH] + "...(truncated)"
|
||||||
logger.info(f"Added text context (original: {len(page_text)}, cleaned: {len(cleaned_text)})")
|
logger.info(f"Added text context (original: {len(page_text)}, cleaned: {len(cleaned_text)})")
|
||||||
|
# Debug: Log first 500 chars of cleaned text to see what AI receives
|
||||||
|
logger.debug(f"Cleaned text preview: {cleaned_text[:500]!r}")
|
||||||
|
else:
|
||||||
|
logger.warning("No page_text provided - AI will only use screenshot")
|
||||||
|
|
||||||
prompt = get_extraction_prompt(cleaned_text if cleaned_text else None)
|
prompt = get_extraction_prompt(cleaned_text if cleaned_text else None)
|
||||||
|
# Debug: Log prompt preview
|
||||||
|
logger.debug(f"Prompt preview (first 300 chars): {prompt[:300]!r}")
|
||||||
|
|
||||||
# Call LLM
|
# Call LLM
|
||||||
response_text = await cls.call_llm(prompt, data_url, config)
|
response_text = await cls.call_llm(prompt, data_url, config)
|
||||||
|
|||||||
@@ -215,13 +215,39 @@ class BrowserlessService:
|
|||||||
except Exception:
|
except Exception:
|
||||||
logger.warning(f"Wait selector {wait_selector} timed out")
|
logger.warning(f"Wait selector {wait_selector} timed out")
|
||||||
|
|
||||||
|
# For Amazon product pages, wait for price to load
|
||||||
|
if "amazon" in url.lower() and "/dp/" in url:
|
||||||
|
amazon_price_selectors = [
|
||||||
|
".a-price .a-offscreen", # Main price element
|
||||||
|
"#corePriceDisplay_desktop_feature_div", # Price section
|
||||||
|
"#corePrice_desktop", # Alternative price container
|
||||||
|
".a-price-whole", # Price number
|
||||||
|
]
|
||||||
|
for selector in amazon_price_selectors:
|
||||||
|
try:
|
||||||
|
await page.wait_for_selector(selector, timeout=5000, state="visible")
|
||||||
|
logger.info(f"Amazon price element found: {selector}")
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
logger.warning("No Amazon price selector found, proceeding anyway")
|
||||||
|
|
||||||
# Wait for network idle to ensure dynamic content loads
|
# Wait for network idle to ensure dynamic content loads
|
||||||
try:
|
try:
|
||||||
await page.wait_for_load_state("networkidle", timeout=5000)
|
await page.wait_for_load_state("networkidle", timeout=5000)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
content = await page.content()
|
# Extract visible text instead of raw HTML
|
||||||
|
# This gets the text as rendered by JavaScript, not the HTML source
|
||||||
|
try:
|
||||||
|
content = await page.inner_text('body')
|
||||||
|
logger.info(f"Extracted {len(content)} chars of visible text from page")
|
||||||
|
except Exception as e:
|
||||||
|
# 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()
|
||||||
|
|
||||||
# Take screenshot if needed for AI analysis
|
# Take screenshot if needed for AI analysis
|
||||||
screenshot_path = ""
|
screenshot_path = ""
|
||||||
@@ -239,9 +265,42 @@ class BrowserlessService:
|
|||||||
filename = f"{safe_name}_{timestamp}.jpg"
|
filename = f"{safe_name}_{timestamp}.jpg"
|
||||||
screenshot_path = f"{screenshots_dir}/{filename}"
|
screenshot_path = f"{screenshots_dir}/{filename}"
|
||||||
|
|
||||||
# Use full_page=True to capture entire page, not just viewport
|
# For Amazon, take focused screenshot of product info area
|
||||||
await page.screenshot(path=screenshot_path, full_page=True, quality=80, type="jpeg")
|
# Full-page screenshots of Amazon are too large and price becomes tiny when resized
|
||||||
logger.info(f"Screenshot saved to {screenshot_path}")
|
is_amazon = "amazon" in url.lower() and "/dp/" in url
|
||||||
|
|
||||||
|
if is_amazon:
|
||||||
|
# Try to screenshot just the main product area containing price
|
||||||
|
try:
|
||||||
|
# Look for main product container
|
||||||
|
product_selectors = [
|
||||||
|
"#dp-container", # Main product container
|
||||||
|
"#ppd", # Product page data
|
||||||
|
"#centerCol", # Center column with price
|
||||||
|
]
|
||||||
|
screenshot_taken = False
|
||||||
|
for selector in product_selectors:
|
||||||
|
try:
|
||||||
|
element = page.locator(selector).first
|
||||||
|
if await element.is_visible():
|
||||||
|
await element.screenshot(path=screenshot_path, quality=85, type="jpeg")
|
||||||
|
logger.info(f"Amazon focused screenshot saved using {selector}")
|
||||||
|
screenshot_taken = True
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not screenshot_taken:
|
||||||
|
# Fallback to viewport screenshot (better than full-page for Amazon)
|
||||||
|
await page.screenshot(path=screenshot_path, full_page=False, quality=80, type="jpeg")
|
||||||
|
logger.info("Amazon viewport screenshot saved (fallback)")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Amazon focused screenshot failed: {e}, using viewport")
|
||||||
|
await page.screenshot(path=screenshot_path, full_page=False, quality=80, type="jpeg")
|
||||||
|
else:
|
||||||
|
# For non-Amazon sites, use full_page to capture entire page
|
||||||
|
await page.screenshot(path=screenshot_path, full_page=True, quality=80, type="jpeg")
|
||||||
|
logger.info(f"Full-page screenshot saved to {screenshot_path}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Failed to take screenshot: {e}")
|
logger.warning(f"Failed to take screenshot: {e}")
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user