SPB Git forge

spb/job-ka

Public
229commits 1branches 0releases
38.1 MBsize
maindefault branch
1 h agolast push
HTML 82.1% Python 14.6% TypeScript 1.9% CSS 1% JavaScript 0.5%

[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

Simon-Pierre Boucher committed 24 days ago (Aug 31, 2026) parent 3ea118a

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