[ka6] fix connecteur cssdd: garde-fou sitemap DigitalRecruiters — une réponse vide/anti-bot (challenge en 200) ne passe plus pour un babillard à 0 offre; retente une fois puis lève une erreur (digitalrecruiters.py, même pattern que njoyn 33dba44)
1 changed file +20 −2
modified
jobka/connectors/digitalrecruiters.py
+20 −2
@@ -5,7 +5,7 @@ | |||||
| 5 | # Fichier : jobka/connectors/digitalrecruiters.py | 5 | # Fichier : jobka/connectors/digitalrecruiters.py | ||
| 6 | # Rôle : Classe de plateforme Cegid Digital Recruiters (sites carrières) — | 6 | # Rôle : Classe de plateforme Cegid Digital Recruiters (sites carrières) — | ||
| 7 | # sitemap + JSON-LD des pages annonce (ex. Santé Québec) | 7 | # sitemap + JSON-LD des pages annonce (ex. Santé Québec) | ||
| 8 | −# Créé : 2026-08-18 Modifié : 2026-08-18 | 8 | +# Créé : 2026-08-18 Modifié : 2026-08-26 | ||
| 9 | # ============================================================================= | 9 | # ============================================================================= | ||
| 10 | """Plateforme Cegid Digital Recruiters. | 10 | """Plateforme Cegid Digital Recruiters. | ||
| 11 | 11 | ||||
@@ -19,6 +19,7 @@ from __future__ import annotations | |||||
| 19 | 19 | ||||
| 20 | import os | 20 | import os | ||
| 21 | import re | 21 | import re | ||
| 22 | +import time | ||||
| 22 | 23 | ||||
| 23 | from ..schema import JobPosting, is_quebec_location | 24 | from ..schema import JobPosting, is_quebec_location | ||
| 24 | from . import _jsonld | 25 | from . import _jsonld | ||
@@ -46,8 +47,25 @@ class DigitalRecruitersConnector(BaseConnector): | |||||
| 46 | node = _jsonld.extract_jobposting(html) | 47 | node = _jsonld.extract_jobposting(html) | ||
| 47 | return _jsonld.jobposting_fields(node) if node else {} | 48 | return _jsonld.jobposting_fields(node) if node else {} | ||
| 48 | 49 | ||||
| 50 | + def _fetch_sitemap(self) -> str: | ||||
| 51 | + """Sitemap de la liste, avec garde-fou : une réponse vide ou anti-bot | ||||
| 52 | + (challenge en 200, escalade en échec) ne doit JAMAIS passer pour un | ||||
| 53 | + babillard à 0 offre — cause des syncs « 0 trouvé » intermittents de | ||||
| 54 | + cssdd. Tout sitemap DR légitime, même sans annonce, contient <urlset> | ||||
| 55 | + — sinon on retente une fois puis on lève (sync en échec, pas 0).""" | ||||
| 56 | + url = f"{self.BASE}/{self.LOCALE}/sitemap.xml" | ||||
| 57 | + for attempt in range(2): | ||||
| 58 | + xml = self.get(url).text | ||||
| 59 | + if _AD_RE.search(xml) or re.search(r"<urlset", xml, re.I): | ||||
| 60 | + return xml | ||||
| 61 | + if attempt == 0: | ||||
| 62 | + time.sleep(10) | ||||
| 63 | + raise RuntimeError( | ||||
| 64 | + f"digitalrecruiters {self.source_id}: sitemap illisible " | ||||
| 65 | + f"(vide ou anti-bot, {len(xml)} octets) — sync avorté") | ||||
| 66 | + | ||||
| 49 | def fetch(self) -> list[JobPosting]: | 67 | def fetch(self) -> list[JobPosting]: | ||
| 50 | − xml = self.get(f"{self.BASE}/{self.LOCALE}/sitemap.xml").text | 68 | + xml = self._fetch_sitemap() | ||
| 51 | out: list[JobPosting] = [] | 69 | out: list[JobPosting] = [] | ||
| 52 | details_used = 0 | 70 | details_used = 0 | ||
| 53 | seen: set[str] = set() | 71 | seen: set[str] = set() | ||
| 54 | 72 | ||||