[ka6] fix connecteur lassonde: site passé derrière Sucuri Cloudproxy (challenge JS en 307 sans Location → 0 offre) — détection Sucuri dans is_blocked (_resilient) + fetch via get_resilient(render_js=True), 32 offres retrouvées
2 changed files +16 −6
modified
jobka/connectors/_resilient.py
+7 −3
@@ -53,13 +53,16 @@ except Exception: # noqa: BLE001 | ||
| 53 | 53 | # 401 = auth manquante, 403/429 = bot bloqué, 5xx CF = challenge/edge). |
| 54 | 54 | BLOCK_STATUS = {403, 429, 503, 520, 521, 522, 523, 524, 526, 1020} |
| 55 | 55 | |
| 56 | −# Marqueurs de page-challenge (Cloudflare/Akamai/Incapsula/PerimeterX/DataDome). | |
| 56 | +# Marqueurs de page-challenge (Cloudflare/Akamai/Incapsula/PerimeterX/DataDome/ | |
| 57 | +# Sucuri). Sucuri Cloudproxy sert son challenge JS en 307 SANS Location | |
| 58 | +# (vu sur lassonde.com 2026-08-31) : injecte un cookie via JS obfusqué. | |
| 57 | 59 | _CHALLENGE_MARKERS = ( |
| 58 | 60 | "just a moment", "cf-browser-verification", "cf-challenge", |
| 59 | 61 | "attention required", "access denied", "request unsuccessful", |
| 60 | 62 | "px-captcha", "perimeterx", "incapsula", "_incapsula_", "datadome", |
| 61 | 63 | "captcha-delivery", "please enable javascript and cookies", |
| 62 | 64 | "checking your browser", "ddos protection by", |
| 65 | + "sucuri_cloudproxy", "you are being redirected", | |
| 63 | 66 | ) |
| 64 | 67 | |
| 65 | 68 | _UA = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " |
@@ -169,8 +172,9 @@ def is_blocked(resp) -> bool: | ||
| 169 | 172 | code = getattr(resp, "status_code", 0) or 0 |
| 170 | 173 | if code in BLOCK_STATUS: |
| 171 | 174 | return True |
| 172 | − # 200 mais page-challenge servie | |
| 173 | − if code == 200: | |
| 175 | + # 200 mais page-challenge servie — ou 307 « sans Location » de Sucuri | |
| 176 | + # (requests ne peut pas la suivre : le corps EST le challenge JS) | |
| 177 | + if code in (200, 307): | |
| 174 | 178 | try: |
| 175 | 179 | body = (resp.text or "")[:4000].lower() |
| 176 | 180 | except Exception: # noqa: BLE001 |
modified
jobka/connectors/lassonde.py
+9 −3
@@ -9,7 +9,11 @@ | ||
| 9 | 9 | # Workforce Now : le centre de carrières ADP de Lassonde ne sert |
| 10 | 10 | # plus que les postes américains (constaté 2026-08-19, found=0 QC), |
| 11 | 11 | # les postes québécois sont affichés sur le site maison. |
| 12 | −# Créé : 2026-08-18 Modifié : 2026-08-19 | |
| 12 | +# 2026-08-31 : lassonde.com est passé derrière Sucuri Cloudproxy | |
| 13 | +# (challenge JS servi en 307 sans Location → 0 offre en direct) — | |
| 14 | +# fetch via get_resilient(render_js=True) : direct d'abord, | |
| 15 | +# escalade Scrapfly ASP+rendu JS si challenge. | |
| 16 | +# Créé : 2026-08-18 Modifié : 2026-08-31 | |
| 13 | 17 | # ============================================================================= |
| 14 | 18 | from __future__ import annotations |
| 15 | 19 | |
@@ -45,7 +49,7 @@ class LassondeConnector(BaseConnector): | ||
| 45 | 49 | def _fetch_detail(self, url: str) -> dict: |
| 46 | 50 | """Le site n'expose pas de JSON-LD JobPosting (seulement un @graph |
| 47 | 51 | WebPage) : la description vient du corps de la page (après le h1).""" |
| 48 | − html = self.get(url).text | |
| 52 | + html = self.get_resilient(url, render_js=True).text | |
| 49 | 53 | node = _jsonld.extract_jobposting(html) |
| 50 | 54 | if node: |
| 51 | 55 | return _jsonld.jobposting_fields(node) |
@@ -59,7 +63,9 @@ class LassondeConnector(BaseConnector): | ||
| 59 | 63 | return {"description_html": m.group(1)[:60000]} |
| 60 | 64 | |
| 61 | 65 | def fetch(self) -> list[JobPosting]: |
| 62 | − page = self.get(self.LIST_URL).text | |
| 66 | + # Sucuri Cloudproxy depuis 2026-08 : rendu JS obligatoire (challenge | |
| 67 | + # cookie), le direct est retenté d'abord à chaque cycle (gratuit) | |
| 68 | + page = self.get_resilient(self.LIST_URL, render_js=True).text | |
| 63 | 69 | out: list[JobPosting] = [] |
| 64 | 70 | details_used = 0 |
| 65 | 71 | seen: set[str] = set() |
| 66 | 72 | |