[ka2] fix connecteur brio: escalade Scrapfly contre le nouveau pare-feu Sucuri CloudProxy. Le sync du 2026-09-08 20:55 échouait franchement (RuntimeError « marqueur hotspot-info absent », garde du fix 2c52899) : le site immeublesbrio.com est passé aujourd'hui de Cloudflare à Sucuri/Cloudproxy, qui répond 307 + challenge JavaScript (cookie sucuri_cloudproxy) à TOUT client sans navigateur, quel que soit l'UA — le cache-buster d'hier ne peut rien contre un challenge côté client, et le wrapper résilient de base.py ne le voit pas (307 hors BLOCK_STATUS, marqueurs sucuri absents). Fix (changement d'angle) : _html() tente toujours le direct puis le cache-buster (gratuits, utiles si le pare-feu est retiré), et escalade vers get_rendered() (Scrapfly ASP + rendu JS, pattern standard du repo) qui passe le challenge — vérifié live : 200, 310 Ko, 49 hotspots ; erreur franche conservée si même Scrapfly ne livre pas le marqueur. Les tentatives directes n'avortent plus sur exception (un futur 403 Sucuri doit escalader, pas propager). Testé : run.py sync brio → found=3 ok (médiane 3 — App. 109 5½ 1 850 $, App. 303 4½ 1 625 $, App. 408 3½ 1 500 $), pm2 restart lou-ka-sync, site :8095 → 200.
1 changed file +20 −13
modified
louka/connectors/brio.py
+20 −13
@@ -30,23 +30,30 @@ class BrioConnector(BaseConnector): | ||
| 30 | 30 | request_delay = 0.6 |
| 31 | 31 | |
| 32 | 32 | def _html(self, url: str, marker: str) -> str: |
| 33 | − """HTML de `url`, avec contournement du cache CDN empoisonné. | |
| 34 | − | |
| 35 | − Vu 2026-09-07 : le cache Cloudflare du site sert une variante | |
| 36 | − brotli figée (content-encoding: br, cf-cache-status HIT) même si | |
| 37 | − le client n'annonce que gzip — requests ne peut pas la décoder et | |
| 38 | − .text devient du binaire (0 annonce parsée, faux « 0 trouvé ok »). | |
| 39 | − Si le marqueur attendu est absent, on force un MISS via un | |
| 40 | − paramètre cache-buster ; s'il manque toujours, échec franc. | |
| 33 | + """HTML de `url`, tolérant au cache CDN empoisonné et au pare-feu Sucuri. | |
| 34 | + | |
| 35 | + Vu 2026-09-07 : le cache Cloudflare du site servait une variante | |
| 36 | + brotli figée (content-encoding: br, cf-cache-status HIT) illisible | |
| 37 | + par requests — contournée par un paramètre cache-buster. | |
| 38 | + Vu 2026-09-08 : le site est passé derrière Sucuri CloudProxy, qui | |
| 39 | + répond 307 avec un challenge JavaScript (cookie sucuri_cloudproxy) | |
| 40 | + à TOUT client sans navigateur — le wrapper résilient de base.py ne | |
| 41 | + le voit pas (307 hors BLOCK_STATUS). On escalade alors vers | |
| 42 | + Scrapfly (ASP + rendu JS), qui passe le challenge ; s'il ne livre | |
| 43 | + pas non plus le marqueur, échec franc. | |
| 41 | 44 | """ |
| 42 | − html = self.get(url).text | |
| 43 | − if marker in html: | |
| 44 | − return html | |
| 45 | − html = self.get(url, params={"lkb": int(time.time())}).text | |
| 45 | + for params in (None, {"lkb": int(time.time())}): | |
| 46 | + try: | |
| 47 | + html = self.get(url, params=params).text | |
| 48 | + except Exception: # noqa: BLE001 — direct bloqué : on escaladera | |
| 49 | + continue | |
| 50 | + if marker in html: | |
| 51 | + return html | |
| 52 | + html = self.get_rendered(url) | |
| 46 | 53 | if marker not in html: |
| 47 | 54 | raise RuntimeError( |
| 48 | 55 | f"brio : contenu inattendu (marqueur {marker!r} absent, " |
| 49 | − f"cache CDN illisible ?) — {url}") | |
| 56 | + f"même via Scrapfly — challenge Sucuri non passé ?) — {url}") | |
| 50 | 57 | return html |
| 51 | 58 | |
| 52 | 59 | def fetch(self) -> list[Listing]: |
| 53 | 60 | |