[ka6] snapshot pré-mission ocadogroup
1 changed file +18 −4
modified
jobka/connectors/guichet_emplois.py
+18 −4
@@ -6,7 +6,7 @@ | |||||
| 6 | # Rôle : Connecteur portail — Guichet-Emplois / Job Bank Canada (offres | 6 | # Rôle : Connecteur portail — Guichet-Emplois / Job Bank Canada (offres | ||
| 7 | # Québec, fenêtre glissante des N derniers jours). AGRÉGATEUR : | 7 | # Québec, fenêtre glissante des N derniers jours). AGRÉGATEUR : | ||
| 8 | # moins autoritaire qu'une page carrière (voir dedup.AGGREGATORS). | 8 | # moins autoritaire qu'une page carrière (voir dedup.AGGREGATORS). | ||
| 9 | −# Créé : 2026-08-18 Modifié : 2026-08-18 | 9 | +# Créé : 2026-08-18 Modifié : 2026-08-30 | ||
| 10 | # ============================================================================= | 10 | # ============================================================================= | ||
| 11 | """Portail Guichet-Emplois (guichetemplois.gc.ca — gouvernement du Canada). | 11 | """Portail Guichet-Emplois (guichetemplois.gc.ca — gouvernement du Canada). | ||
| 12 | 12 | ||||
@@ -36,6 +36,9 @@ DAYS = os.environ.get("JOBKA_GUICHET_DAYS", "7") | |||||
| 36 | MAX_PAGES = int(os.environ.get("JOBKA_GUICHET_PAGES", "20")) | 36 | MAX_PAGES = int(os.environ.get("JOBKA_GUICHET_PAGES", "20")) | ||
| 37 | MAX_DETAILS = int(os.environ.get("JOBKA_GUICHET_DETAIL_LIMIT", "50")) | 37 | MAX_DETAILS = int(os.environ.get("JOBKA_GUICHET_DETAIL_LIMIT", "50")) | ||
| 38 | 38 | ||||
| 39 | +# Page « Erreur HTTP 500 » servie avec un statut 200 (vu 2026-08-30, aléatoire | ||||
| 40 | +# requête par requête) : sans détection, 0 item → fin de pagination prématurée. | ||||
| 41 | +_SOFT500_RE = re.compile(r"<title>\s*(?:Erreur HTTP|HTTP Error)\s*5\d\d", re.I) | ||||
| 39 | _ITEM_RE = re.compile( | 42 | _ITEM_RE = re.compile( | ||
| 40 | r'href="/rechercheemplois/offredemploi/(\d+)[^"]*"[^>]*class="resultJobItem"' | 43 | r'href="/rechercheemplois/offredemploi/(\d+)[^"]*"[^>]*class="resultJobItem"' | ||
| 41 | r"(.*?)</ul>", re.S) | 44 | r"(.*?)</ul>", re.S) | ||
@@ -60,6 +63,8 @@ class GuichetEmploisConnector(BaseConnector): | |||||
| 60 | 63 | ||||
| 61 | def _fetch_detail(self, jid: str) -> dict: | 64 | def _fetch_detail(self, jid: str) -> dict: | ||
| 62 | html = self.get(f"{BASE}/rechercheemplois/offredemploi/{jid}").text | 65 | html = self.get(f"{BASE}/rechercheemplois/offredemploi/{jid}").text | ||
| 66 | + if _SOFT500_RE.search(html): | ||||
| 67 | + return {} # erreur 200 : rien à cacher, repris au prochain cycle | ||||
| 63 | d: dict = {} | 68 | d: dict = {} | ||
| 64 | m = re.search(r"<main[^>]*>(.*?)</main>", html, re.S | re.I) | 69 | m = re.search(r"<main[^>]*>(.*?)</main>", html, re.S | re.I) | ||
| 65 | body = m.group(1) if m else "" | 70 | body = m.group(1) if m else "" | ||
@@ -94,21 +99,30 @@ class GuichetEmploisConnector(BaseConnector): | |||||
| 94 | seen: set[str] = set() | 99 | seen: set[str] = set() | ||
| 95 | for page in range(1, MAX_PAGES + 1): | 100 | for page in range(1, MAX_PAGES + 1): | ||
| 96 | html = "" | 101 | html = "" | ||
| 97 | − for attempt in (1, 2): | 102 | + for attempt in (1, 2, 3): | ||
| 98 | try: | 103 | try: | ||
| 99 | html = self.get(f"{BASE}/jobsearch/rechercheemplois", | 104 | html = self.get(f"{BASE}/jobsearch/rechercheemplois", | ||
| 100 | params={"fprov": "QC", "sort": "D", | 105 | params={"fprov": "QC", "sort": "D", | ||
| 101 | "fage": DAYS, | 106 | "fage": DAYS, | ||
| 102 | "page": str(page)}).text | 107 | "page": str(page)}).text | ||
| 103 | − break | ||||
| 104 | except (requests.ConnectionError, requests.Timeout): | 108 | except (requests.ConnectionError, requests.Timeout): | ||
| 105 | # le WAF du Guichet coupe parfois la connexion : une | 109 | # le WAF du Guichet coupe parfois la connexion : une | ||
| 106 | # reprise après pause, sinon on garde l'acquis partiel | 110 | # reprise après pause, sinon on garde l'acquis partiel | ||
| 107 | − if attempt == 2: | 111 | + if attempt == 3: | ||
| 108 | if out: | 112 | if out: | ||
| 109 | return out | 113 | return out | ||
| 110 | raise | 114 | raise | ||
| 111 | time.sleep(25) | 115 | time.sleep(25) | ||
| 116 | + continue | ||||
| 117 | + if _SOFT500_RE.search(html): | ||||
| 118 | + # « Erreur HTTP 500 » servie en 200 : retenter la page | ||||
| 119 | + html = "" | ||||
| 120 | + if attempt < 3: | ||||
| 121 | + time.sleep(15) | ||||
| 122 | + continue | ||||
| 123 | + break | ||||
| 124 | + if not html: | ||||
| 125 | + continue # erreur persistante sur CETTE page : suivante | ||||
| 112 | items = _ITEM_RE.findall(html) | 126 | items = _ITEM_RE.findall(html) | ||
| 113 | if not items: | 127 | if not items: | ||
| 114 | break | 128 | break | ||
| 115 | 129 | ||||