SPB Git forge

spb/lou-ka

Public

Lou·Ka — tous les logements à louer du Québec, un seul endroit.

232commits 1branches 0releases
172.9 MBsize
maindefault branch
2 days agolast push
HTML 98.9% Python 0.6%

[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.

Simon-Pierre Boucher committed 16 days ago (Sep 9, 2026) parent a677a1a

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