mirror of
https://github.com/R0m1k3/Priceflow.git
synced 2026-10-14 18:56:43 +02:00
fix: Improve domain matching and add discover-all endpoint
- Improve domain matching in direct_search_service to handle various formats - Add /discover-all endpoint to discover search URLs for all sites missing one - Add constants for magic numbers and fix linting issues - Better domain cleaning in comparison functions
This commit is contained in:
2 files changed
+80
-8
No files matched your search
@@ -151,3 +151,40 @@ async def discover_site_search_url(
|
||||
"success": False,
|
||||
"message": "Impossible de découvrir l'URL de recherche automatiquement",
|
||||
}
|
||||
|
||||
|
||||
@router.post("/discover-all", response_model=dict)
|
||||
async def discover_all_missing_search_urls(
|
||||
background_tasks: BackgroundTasks,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Lance la découverte d'URL de recherche pour tous les sites qui n'en ont pas.
|
||||
La découverte se fait en arrière-plan.
|
||||
"""
|
||||
sites = search_service.get_all_sites(db)
|
||||
sites_to_discover = [s for s in sites if not s.search_url]
|
||||
|
||||
if not sites_to_discover:
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Tous les sites ont déjà une URL de recherche configurée",
|
||||
"sites_queued": 0,
|
||||
}
|
||||
|
||||
db_url = os.getenv("DATABASE_URL", "")
|
||||
|
||||
for site in sites_to_discover:
|
||||
background_tasks.add_task(
|
||||
_discover_and_update_site,
|
||||
site.id,
|
||||
site.domain,
|
||||
db_url,
|
||||
)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Découverte lancée pour {len(sites_to_discover)} sites",
|
||||
"sites_queued": len(sites_to_discover),
|
||||
"sites": [{"id": s.id, "domain": s.domain} for s in sites_to_discover],
|
||||
}
|
||||
@@ -16,6 +16,13 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
BROWSERLESS_URL = os.getenv("BROWSERLESS_URL", "ws://browserless:3000")
|
||||
|
||||
# Constants
|
||||
MIN_TITLE_LENGTH = 3
|
||||
USER_AGENT = (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
||||
"(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
)
|
||||
|
||||
|
||||
class SearchResult:
|
||||
"""Résultat de recherche"""
|
||||
@@ -157,7 +164,7 @@ async def search(
|
||||
return all_results[:max_results]
|
||||
|
||||
|
||||
async def _search_site_browserless(
|
||||
async def _search_site_browserless( # noqa: PLR0912, PLR0915
|
||||
query: str,
|
||||
site: dict,
|
||||
max_results: int,
|
||||
@@ -190,7 +197,7 @@ async def _search_site_browserless(
|
||||
try:
|
||||
context = await browser.new_context(
|
||||
viewport={"width": 1920, "height": 1080},
|
||||
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
user_agent=USER_AGENT,
|
||||
locale="fr-FR",
|
||||
)
|
||||
page = await context.new_page()
|
||||
@@ -272,7 +279,7 @@ async def _search_site_browserless(
|
||||
|
||||
# Extraire le titre
|
||||
title = _extract_title(link)
|
||||
if not title or len(title) < 3:
|
||||
if not title or len(title) < MIN_TITLE_LENGTH:
|
||||
continue
|
||||
|
||||
results.append(SearchResult(
|
||||
@@ -295,24 +302,52 @@ async def _search_site_browserless(
|
||||
|
||||
def _get_default_search_url(domain: str) -> str | None:
|
||||
"""Retourne l'URL de recherche par défaut pour un domaine"""
|
||||
# Nettoyer le domaine pour la comparaison
|
||||
clean = domain.lower().strip()
|
||||
if clean.startswith("www."):
|
||||
clean = clean[4:]
|
||||
|
||||
for key, config in DEFAULT_SITE_CONFIGS.items():
|
||||
if key in domain or domain in key:
|
||||
key_clean = key.lower().strip()
|
||||
if key_clean.startswith("www."):
|
||||
key_clean = key_clean[4:]
|
||||
|
||||
# Comparaison exacte ou partielle
|
||||
if clean == key_clean or key_clean in clean or clean in key_clean:
|
||||
return config.get("search_url")
|
||||
return None
|
||||
|
||||
|
||||
def _get_default_product_selector(domain: str) -> str | None:
|
||||
"""Retourne le sélecteur de produits par défaut pour un domaine"""
|
||||
# Nettoyer le domaine pour la comparaison
|
||||
clean = domain.lower().strip()
|
||||
if clean.startswith("www."):
|
||||
clean = clean[4:]
|
||||
|
||||
for key, config in DEFAULT_SITE_CONFIGS.items():
|
||||
if key in domain or domain in key:
|
||||
key_clean = key.lower().strip()
|
||||
if key_clean.startswith("www."):
|
||||
key_clean = key_clean[4:]
|
||||
|
||||
if clean == key_clean or key_clean in clean or clean in key_clean:
|
||||
return config.get("product_selector")
|
||||
return None
|
||||
|
||||
|
||||
def _get_default_wait_selector(domain: str) -> str | None:
|
||||
"""Retourne le sélecteur d'attente par défaut pour un domaine"""
|
||||
# Nettoyer le domaine pour la comparaison
|
||||
clean = domain.lower().strip()
|
||||
if clean.startswith("www."):
|
||||
clean = clean[4:]
|
||||
|
||||
for key, config in DEFAULT_SITE_CONFIGS.items():
|
||||
if key in domain or domain in key:
|
||||
key_clean = key.lower().strip()
|
||||
if key_clean.startswith("www."):
|
||||
key_clean = key_clean[4:]
|
||||
|
||||
if clean == key_clean or key_clean in clean or clean in key_clean:
|
||||
return config.get("wait_selector")
|
||||
return None
|
||||
|
||||
@@ -359,12 +394,12 @@ def _extract_title(element) -> str:
|
||||
"""Extrait le titre d'un élément"""
|
||||
# Essayer différentes méthodes
|
||||
title = element.get("title", "")
|
||||
if title and len(title) > 3:
|
||||
if title and len(title) > MIN_TITLE_LENGTH:
|
||||
return title.strip()
|
||||
|
||||
# Texte de l'élément
|
||||
text = element.get_text(strip=True)
|
||||
if text and len(text) > 3:
|
||||
if text and len(text) > MIN_TITLE_LENGTH:
|
||||
return text[:200]
|
||||
|
||||
# Attribut aria-label
|
||||
|
||||
Reference in new issue
Block a user