[ka2] fix connecteur douek: retry (3 essais, backoff 5/10 s) de la page d'accueil — un raté transitoire (vu 2026-08-30 ~10:00) produisait un faux « 0 trouvé ok » (médiane 10) car fetch() retournait [] en silence si l'accueil n'avait aucune carte featured_property et avalait les exceptions par immeuble; désormais erreur franche si aucune carte après 3 essais ou si des immeubles sont en erreur sans aucune annonce récoltée. Testé: run.py sync douek → 10 annonces (= médiane 10)
1 changed file +43 −21
modified
louka/connectors/douek.py
+43 −21
@@ -13,6 +13,7 @@ | ||
| 13 | 13 | from __future__ import annotations |
| 14 | 14 | |
| 15 | 15 | import re |
| 16 | +import time | |
| 16 | 17 | |
| 17 | 18 | from bs4 import BeautifulSoup |
| 18 | 19 | |
@@ -41,35 +42,56 @@ class DouekConnector(AlfidConnector): | ||
| 41 | 42 | max_buildings = 20 # garde-fou (9 immeubles à l'écriture) |
| 42 | 43 | |
| 43 | 44 | def fetch(self) -> list[Listing]: |
| 44 | − home = self.get(f"{SITE}/").text | |
| 45 | − soup = BeautifulSoup(home, "html.parser") | |
| 46 | − | |
| 47 | − # cartes d'immeubles de l'accueil : slug, nom, adresse | |
| 45 | + # Un raté transitoire (vu 2026-08-30 ~10:00) produisait un faux | |
| 46 | + # « 0 trouvé ok » (médiane 10) : retry de l'accueil (3 essais, | |
| 47 | + # backoff 5/10 s) puis erreur franche plutôt qu'un zéro silencieux. | |
| 48 | 48 | buildings: dict[str, dict] = {} |
| 49 | − for card in soup.select(".featured_property"): | |
| 50 | − a = card.select_one('a[href*="/rent/"]') | |
| 51 | − if not a: | |
| 52 | − continue | |
| 53 | − slug = (a.get("href") or "").rstrip("/").split("/")[-1] | |
| 54 | − if not slug or slug == "cities": | |
| 55 | − continue | |
| 56 | − name_el = card.select_one(".building-name") | |
| 57 | − addr_el = card.select_one(".building-address") | |
| 58 | − buildings.setdefault(slug, { | |
| 59 | − "name": _clean(name_el.get_text(" ", strip=True) | |
| 60 | − if name_el else slug), | |
| 61 | − "address": _clean(addr_el.get_text(" ", strip=True) | |
| 62 | − if addr_el else ""), | |
| 63 | − }) | |
| 49 | + for attempt in range(3): | |
| 50 | + try: | |
| 51 | + home = self.get(f"{SITE}/").text | |
| 52 | + except Exception: | |
| 53 | + if attempt == 2: | |
| 54 | + raise | |
| 55 | + home = "" | |
| 56 | + soup = BeautifulSoup(home, "html.parser") | |
| 57 | + # cartes d'immeubles de l'accueil : slug, nom, adresse | |
| 58 | + for card in soup.select(".featured_property"): | |
| 59 | + a = card.select_one('a[href*="/rent/"]') | |
| 60 | + if not a: | |
| 61 | + continue | |
| 62 | + slug = (a.get("href") or "").rstrip("/").split("/")[-1] | |
| 63 | + if not slug or slug == "cities": | |
| 64 | + continue | |
| 65 | + name_el = card.select_one(".building-name") | |
| 66 | + addr_el = card.select_one(".building-address") | |
| 67 | + buildings.setdefault(slug, { | |
| 68 | + "name": _clean(name_el.get_text(" ", strip=True) | |
| 69 | + if name_el else slug), | |
| 70 | + "address": _clean(addr_el.get_text(" ", strip=True) | |
| 71 | + if addr_el else ""), | |
| 72 | + }) | |
| 73 | + if buildings: | |
| 74 | + break | |
| 75 | + if attempt < 2: | |
| 76 | + time.sleep(5 * (attempt + 1)) | |
| 77 | + if not buildings: | |
| 78 | + raise RuntimeError( | |
| 79 | + "accueil douek.ca sans carte featured_property après 3 essais") | |
| 64 | 80 | |
| 65 | 81 | listings: list[Listing] = [] |
| 82 | + errors = 0 | |
| 83 | + last_exc: Exception | None = None | |
| 66 | 84 | for i, (slug, meta) in enumerate(buildings.items()): |
| 67 | 85 | if i >= self.max_buildings: |
| 68 | 86 | break |
| 69 | 87 | try: |
| 70 | 88 | listings.extend(self._parse_rent_page(slug, meta)) |
| 71 | − except Exception: | |
| 72 | − continue | |
| 89 | + except Exception as exc: | |
| 90 | + errors += 1 | |
| 91 | + last_exc = exc | |
| 92 | + # tous les immeubles en erreur et rien récolté : erreur franche | |
| 93 | + if not listings and last_exc is not None and errors: | |
| 94 | + raise last_exc | |
| 73 | 95 | return listings |
| 74 | 96 | |
| 75 | 97 | # -- fiche /rent/<slug> : suites rendues serveur ----------------------------- |
| 76 | 98 | |