mirror of
https://github.com/R0m1k3/Priceflow.git
synced 2026-10-22 06:38:12 +02:00
3 files changed
+60
-16
No files matched your search
@@ -49,13 +49,17 @@ POPUP_SELECTORS = [
|
|||||||
"button:has-text('Tout accepter')",
|
"button:has-text('Tout accepter')",
|
||||||
"button:has-text('Accepter')",
|
"button:has-text('Accepter')",
|
||||||
"input[aria-labelledby='sp-cc-accept-label']",
|
"input[aria-labelledby='sp-cc-accept-label']",
|
||||||
# Amazon interstitials
|
"#nav-flyout-prime button",
|
||||||
|
|
||||||
|
# Amazon Interstitials (Soft blocks)
|
||||||
"button:has-text('Continuer les achats')",
|
"button:has-text('Continuer les achats')",
|
||||||
"span:has-text('Continuer les achats')",
|
"span:has-text('Continuer les achats')",
|
||||||
"a:has-text('Continuer les achats')",
|
"a:has-text('Continuer les achats')",
|
||||||
"input[value='Continuer les achats']",
|
"input[value='Continuer les achats']",
|
||||||
"input[value='Continue shopping']",
|
"input[value='Continue shopping']",
|
||||||
|
"span.a-button-inner > input.a-button-input[type='submit']",
|
||||||
"form:has-text('Continuer les achats') input[type='submit']",
|
"form:has-text('Continuer les achats') input[type='submit']",
|
||||||
|
"[aria-labelledby='continue-shopping-label']",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -200,21 +204,42 @@ class BrowserlessService:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def _handle_popups(page: Page):
|
async def _handle_popups(page: Page):
|
||||||
"""Attempt to close popups and cookie banners."""
|
"""Attempt to close popups and cookie banners with retry logic."""
|
||||||
logger.debug("Attempting to close popups...")
|
logger.debug("Attempting to close popups...")
|
||||||
|
|
||||||
|
# First pass: Try to close all visible popups
|
||||||
|
closed_popups = []
|
||||||
for popup_selector in POPUP_SELECTORS:
|
for popup_selector in POPUP_SELECTORS:
|
||||||
try:
|
try:
|
||||||
if await page.locator(popup_selector).count() > 0:
|
if await page.locator(popup_selector).count() > 0:
|
||||||
logger.info(f"🚫 Closing popup: {popup_selector}")
|
logger.info(f"🚫 Closing popup: {popup_selector}")
|
||||||
await page.locator(popup_selector).first.click(timeout=2000)
|
await page.locator(popup_selector).first.click(timeout=2000)
|
||||||
await page.wait_for_timeout(500)
|
closed_popups.append(popup_selector)
|
||||||
|
await page.wait_for_timeout(1000)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
# Try Escape key multiple times
|
||||||
await page.keyboard.press("Escape")
|
for _ in range(2):
|
||||||
except Exception:
|
try:
|
||||||
pass
|
await page.keyboard.press("Escape")
|
||||||
|
await page.wait_for_timeout(500)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Second pass: Verify and retry if any popups are still visible
|
||||||
|
if closed_popups:
|
||||||
|
logger.info(f"Verifying {len(closed_popups)} closed popup(s)...")
|
||||||
|
await page.wait_for_timeout(1000)
|
||||||
|
|
||||||
|
for popup_selector in closed_popups:
|
||||||
|
try:
|
||||||
|
if await page.locator(popup_selector).count() > 0:
|
||||||
|
logger.warning(f"Popup reappeared, retrying: {popup_selector}")
|
||||||
|
await page.locator(popup_selector).first.click(timeout=2000)
|
||||||
|
await page.wait_for_timeout(1000)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def _extract_amazon_price(page: Page) -> str:
|
async def _extract_amazon_price(page: Page) -> str:
|
||||||
|
|||||||
@@ -16,15 +16,28 @@ class ItemService:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_items(db: Session):
|
def get_items(db: Session):
|
||||||
items = db.query(models.Item).all()
|
items = db.query(models.Item).all()
|
||||||
return [
|
result = []
|
||||||
{
|
for item in items:
|
||||||
**item.__dict__,
|
# Get latest price history with screenshot
|
||||||
"screenshot_url": f"/screenshots/item_{item.id}.png"
|
latest_history = (
|
||||||
if os.path.exists(f"screenshots/item_{item.id}.png")
|
db.query(models.PriceHistory)
|
||||||
else None,
|
.filter(models.PriceHistory.item_id == item.id)
|
||||||
}
|
.filter(models.PriceHistory.screenshot_path.isnot(None))
|
||||||
for item in items
|
.order_by(models.PriceHistory.timestamp.desc())
|
||||||
]
|
.first()
|
||||||
|
)
|
||||||
|
|
||||||
|
screenshot_url = None
|
||||||
|
if latest_history and latest_history.screenshot_path:
|
||||||
|
# Convert absolute path /app/screenshots/... to URL /screenshots/...
|
||||||
|
filename = os.path.basename(latest_history.screenshot_path)
|
||||||
|
screenshot_url = f"/screenshots/{filename}"
|
||||||
|
elif os.path.exists(f"screenshots/item_{item.id}.png"):
|
||||||
|
# Fallback to legacy static file
|
||||||
|
screenshot_url = f"/screenshots/item_{item.id}.png"
|
||||||
|
|
||||||
|
result.append({**item.__dict__, "screenshot_url": screenshot_url})
|
||||||
|
return result
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_item(db: Session, item: schemas.ItemCreate):
|
def create_item(db: Session, item: schemas.ItemCreate):
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ POPUP_SELECTORS = [
|
|||||||
"input[aria-labelledby='sp-cc-accept-label']",
|
"input[aria-labelledby='sp-cc-accept-label']",
|
||||||
"#nav-flyout-prime button", # Prime flyout
|
"#nav-flyout-prime button", # Prime flyout
|
||||||
|
|
||||||
|
# Amazon Interstitials (Soft blocks)
|
||||||
|
"button:has-text('Continuer les achats')",
|
||||||
|
"input[value='Continuer les achats']",
|
||||||
|
"span.a-button-inner > input.a-button-input[type='submit']",
|
||||||
|
"[aria-labelledby='continue-shopping-label']",
|
||||||
|
|
||||||
# French RGPD/Cookie consent platforms
|
# French RGPD/Cookie consent platforms
|
||||||
"#axeptio_btn_acceptAll", # Axeptio
|
"#axeptio_btn_acceptAll", # Axeptio
|
||||||
"#axeptio_main_button",
|
"#axeptio_main_button",
|
||||||
|
|||||||
Reference in new issue
Block a user