SPB Git

spb/job-ka

Public

[ka6] connecteur cusm_ir: diagnostic du mur Radware hCaptcha host-wide *.njoyn.com (2026-09-02) — non contournable (Scrapfly/BrightData/Oxylabs/Chromium réel tous bloqués), erreur explicite via _radware_hint, offres en base préservées

Simon-Pierre Boucher committed (Sep 4, 2026) parent 7217386 Browse files

1 changed file +32 −2

Unified Split
modified jobka/connectors/njoyn.py +32 −2
@@ -5,7 +5,7 @@
5 # Fichier : jobka/connectors/njoyn.py 5 # Fichier : jobka/connectors/njoyn.py
6 # Rôle : Classe de plateforme Njoyn (CGI) — très répandu au secteur public 6 # Rôle : Classe de plateforme Njoyn (CGI) — très répandu au secteur public
7 # québécois (santé, villes, sociétés d'État). HTML serveur + jeton. 7 # québécois (santé, villes, sociétés d'État). HTML serveur + jeton.
8−# Créé : 2026-08-18 Modifié : 2026-08-26 8+# Créé : 2026-08-18 Modifié : 2026-09-04
9 # ============================================================================= 9 # =============================================================================
10 """Plateforme Njoyn (CGI). 10 """Plateforme Njoyn (CGI).
11 11
@@ -20,6 +20,18 @@ une ligne de tableau (habillage classique) pointant vers
20 tenants, mettre ``USE_SCRAPFLY = True`` (contournement ASP). Les babillards 20 tenants, mettre ``USE_SCRAPFLY = True`` (contournement ASP). Les babillards
21 servis sur un domaine personnalisé (ex. emplois.santemontreal.qc.ca) se 21 servis sur un domaine personnalisé (ex. emplois.santemontreal.qc.ca) se
22 consultent directement. 22 consultent directement.
23+
24+⚠⚠ Incident 2026-09-02 : CGI a durci Radware sur TOUT *.njoyn.com — mur
25+hCaptcha INTERACTIF (validate.perfdrive.com) servi à chaque nouveau
26+visiteur, humains compris. Toute la pile d'escalade échoue : Scrapfly
27+(ASP, render_js, pool résidentiel → page captcha ou 422), Bright Data
28+(reject_block « captcha or protection page found »), Oxylabs (Realtime
29+render + proxies résidentiels -cc-CA), et même un vrai Chromium headed
30+(le clic sur la case hCaptcha ouvre un puzzle imagé). Rien à contourner
31+côté code : ``_fetch_listing`` identifie le mur dans son message d'erreur
32+et le sync reste en échec (les offres en base ne sont pas retirées).
33+Surveiller la levée du mur — les tenants sur domaine personnalisé
34+(sante_montreal, stl_laval, villes) ne sont PAS touchés.
23 """ 35 """
24 from __future__ import annotations 36 from __future__ import annotations
25 37
@@ -255,6 +267,23 @@ class NjoynConnector(BaseConnector):
255 it.get("fields", {}).get("villes et arrondissements") or "") 267 it.get("fields", {}).get("villes et arrondissements") or "")
256 return bool(place) and is_quebec_location(place.replace("-", " ")) 268 return bool(place) and is_quebec_location(place.replace("-", " "))
257 269
270+ def _radware_hint(self) -> str:
271+ """Diagnostic post-échec : sonde la page liste en direct (une seule
272+ requête, sans Scrapfly) pour vérifier si le domaine sert le mur
273+ hCaptcha Radware (redirection validate.perfdrive.com). Voir le
274+ docstring du module — incident host-wide *.njoyn.com du 2026-09-02,
275+ aucune voie de contournement dans la pile d'escalade."""
276+ try:
277+ r = self.get(self._url("joblisting"))
278+ if "perfdrive" in (r.url or "") or "Radware" in r.text[:4000]:
279+ return (" — cause identifiée : mur anti-bot Radware "
280+ "(hCaptcha interactif, validate.perfdrive.com) sur "
281+ "tout *.njoyn.com; pile d'escalade complète en échec "
282+ "(vu 2026-09-02→), attendre la levée du mur")
283+ except Exception:
284+ pass
285+ return ""
286+
258 def _fetch_listing(self) -> tuple[str, list[dict]]: 287 def _fetch_listing(self) -> tuple[str, list[dict]]:
259 """Page liste + items parsés, avec garde-fou : une réponse vide ou 288 """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 289 anti-bot (Scrapfly en échec renvoie "" — voir base.get_scrapfly) ne
@@ -271,7 +300,8 @@ class NjoynConnector(BaseConnector):
271 time.sleep(10) 300 time.sleep(10)
272 raise RuntimeError( 301 raise RuntimeError(
273 f"njoyn {self.source_id}: page liste illisible " 302 f"njoyn {self.source_id}: page liste illisible "
274− f"(vide ou anti-bot, {len(page)} octets) — sync avorté") 303+ f"(vide ou anti-bot, {len(page)} octets) — sync avorté"
304+ + self._radware_hint())
275 305
276 def fetch(self) -> list[JobPosting]: 306 def fetch(self) -> list[JobPosting]:
277 page, items = self._fetch_listing() 307 page, items = self._fetch_listing()
278 308
Menu

Theme