SPB Git

Repository Public

Commit

[ka2] fix connecteur solaris: retry anti-hoquet — la page sert parfois « 6 unités libres » (data-count=6) avec une liste vide/tronquée. 2e occurrence en 3 jours (2026-10-16 07:12 et 2026-10-19 20:08, sync_log 169676) : le garde-fou anti zéro silencieux levait une erreur franche (correct, pas de dépublication) mais marquait le connecteur stale à chaque hoquet. Fix minimal : fetch() réessaie jusqu'à 3 fois (pause 4 s/8 s) avec cache-buster ?lkb=<ts> sur les tentatives 2-3 (site derrière Cloudflare — pattern du fix brio 2c52899) avant de lever l'erreur ; cache-buster absent de la 1re tentative pour préserver la clé de rejeu des fixtures (URL nue). Garde-fous inchangés : section data-live="units" absente ou 0 unité avec data-count>0 = erreur FRANCHE après les retries ; data-count=0 = vrai zéro accepté. Testé : pytest solaris 2 passed, run.py sync solaris → found=6 ok (0,2 s, médiane 6.0), pm2 restart lou-ka-sync, site :8095 → 200.

Simon-Pierre Boucher committed · Oct 20, 2026 parent 57646d3
1 changed file +21 −1 Browse files
modified louka/connectors/solaris.py +21 −1
@@ -13,6 +13,7 @@ from __future__ import annotations
13 13
14 14 import html as _html
15 15 import re
16 +import time
16 17
17 18 from ..schema import Listing
18 19 from .base import BaseConnector
@@ -47,9 +48,28 @@ class SolarisConnector(BaseConnector):
47 48 max_images = 10
48 49
49 50 def fetch(self) -> list[Listing]:
51 + # Le serveur sert PARFOIS la page avec l'en-tête « N unités libres »
52 + # (data-count>0) mais une liste d'unités vide/tronquée — hoquets
53 + # constatés 2026-10-16 et 2026-10-19. Retry avec cache-buster
54 + # (site derrière Cloudflare, pattern du fix brio 2c52899) avant de
55 + # lever l'erreur franche. Cache-buster seulement sur les tentatives
56 + # 2-3 : la clé de rejeu des fixtures reste l'URL nue.
57 + last_err: RuntimeError | None = None
58 + for attempt in range(3):
59 + if attempt:
60 + time.sleep(4 * attempt)
61 + url = PLANS_URL if attempt == 0 else \
62 + f"{PLANS_URL}?lkb={int(time.time())}"
63 + try:
64 + return self._fetch_once(url)
65 + except RuntimeError as err:
66 + last_err = err
67 + raise last_err
68 +
69 + def _fetch_once(self, url: str) -> list[Listing]:
50 70 # le serveur n'annonce pas de charset → requests décoderait en
51 71 # latin-1 et casserait « ½ », « ² » et les espaces insécables des prix.
52 − raw = self.get(PLANS_URL).content.decode("utf-8", "replace")
72 + raw = self.get(url).content.decode("utf-8", "replace")
53 73
54 74 m = SECTION_RE.search(raw)
55 75 if not m:
56 76
Menu

Theme