[ka6] fix connecteur stl_laval: garde-fou njoyn — une page liste vide/anti-bot (Scrapfly en échec renvoie "") ne passe plus pour un babillard à 0 offre; retente une fois puis lève une erreur (njoyn.py)
1 changed file +21 −4
modified
jobka/connectors/njoyn.py
+21 −4
@@ -5,7 +5,7 @@ | ||
| 5 | 5 | # Fichier : jobka/connectors/njoyn.py |
| 6 | 6 | # Rôle : Classe de plateforme Njoyn (CGI) — très répandu au secteur public |
| 7 | 7 | # québécois (santé, villes, sociétés d'État). HTML serveur + jeton. |
| 8 | −# Créé : 2026-08-18 Modifié : 2026-08-25 | |
| 8 | +# Créé : 2026-08-18 Modifié : 2026-08-26 | |
| 9 | 9 | # ============================================================================= |
| 10 | 10 | """Plateforme Njoyn (CGI). |
| 11 | 11 | |
@@ -27,6 +27,7 @@ import hashlib | ||
| 27 | 27 | import html as _html |
| 28 | 28 | import os |
| 29 | 29 | import re |
| 30 | +import time | |
| 30 | 31 | |
| 31 | 32 | from ..schema import JobPosting, clean_html |
| 32 | 33 | from .base import BaseConnector |
@@ -254,10 +255,26 @@ class NjoynConnector(BaseConnector): | ||
| 254 | 255 | it.get("fields", {}).get("villes et arrondissements") or "") |
| 255 | 256 | return bool(place) and is_quebec_location(place.replace("-", " ")) |
| 256 | 257 | |
| 258 | + def _fetch_listing(self) -> tuple[str, list[dict]]: | |
| 259 | + """Page liste + items parsés, avec garde-fou : une réponse vide ou | |
| 260 | + anti-bot (Scrapfly en échec renvoie "" — voir base.get_scrapfly) ne | |
| 261 | + doit JAMAIS passer pour un babillard vide. Toute page Njoyn légitime, | |
| 262 | + même sans offre, contient le formulaire xweb.asp — sinon on retente | |
| 263 | + une fois puis on lève une erreur (sync en échec, pas « 0 trouvé »).""" | |
| 264 | + for attempt in range(2): | |
| 265 | + page = self._html(self._url("joblisting")) | |
| 266 | + items = (self._parse_articles(page) or self._parse_table(page) | |
| 267 | + or self._parse_rows(page)) | |
| 268 | + if items or re.search(r"xweb\.asp", page, re.I): | |
| 269 | + return page, items | |
| 270 | + if attempt == 0: | |
| 271 | + time.sleep(10) | |
| 272 | + raise RuntimeError( | |
| 273 | + f"njoyn {self.source_id}: page liste illisible " | |
| 274 | + f"(vide ou anti-bot, {len(page)} octets) — sync avorté") | |
| 275 | + | |
| 257 | 276 | def fetch(self) -> list[JobPosting]: |
| 258 | − page = self._html(self._url("joblisting")) | |
| 259 | − items = (self._parse_articles(page) or self._parse_table(page) | |
| 260 | − or self._parse_rows(page)) | |
| 277 | + page, items = self._fetch_listing() | |
| 261 | 278 | items = self._paginate(page, items) |
| 262 | 279 | items = [it for it in items if self._keep(it)] |
| 263 | 280 | out: list[JobPosting] = [] |
| 264 | 281 | |