[ka6] snapshot pré-mission ocadogroup
1 changed file +18 −4
modified
jobka/connectors/guichet_emplois.py
+18 −4
@@ -6,7 +6,7 @@ | ||
| 6 | 6 | # Rôle : Connecteur portail — Guichet-Emplois / Job Bank Canada (offres |
| 7 | 7 | # Québec, fenêtre glissante des N derniers jours). AGRÉGATEUR : |
| 8 | 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 | 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 | 36 | MAX_PAGES = int(os.environ.get("JOBKA_GUICHET_PAGES", "20")) |
| 37 | 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 | 42 | _ITEM_RE = re.compile( |
| 40 | 43 | r'href="/rechercheemplois/offredemploi/(\d+)[^"]*"[^>]*class="resultJobItem"' |
| 41 | 44 | r"(.*?)</ul>", re.S) |
@@ -60,6 +63,8 @@ class GuichetEmploisConnector(BaseConnector): | ||
| 60 | 63 | |
| 61 | 64 | def _fetch_detail(self, jid: str) -> dict: |
| 62 | 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 | 68 | d: dict = {} |
| 64 | 69 | m = re.search(r"<main[^>]*>(.*?)</main>", html, re.S | re.I) |
| 65 | 70 | body = m.group(1) if m else "" |
@@ -94,21 +99,30 @@ class GuichetEmploisConnector(BaseConnector): | ||
| 94 | 99 | seen: set[str] = set() |
| 95 | 100 | for page in range(1, MAX_PAGES + 1): |
| 96 | 101 | html = "" |
| 97 | − for attempt in (1, 2): | |
| 102 | + for attempt in (1, 2, 3): | |
| 98 | 103 | try: |
| 99 | 104 | html = self.get(f"{BASE}/jobsearch/rechercheemplois", |
| 100 | 105 | params={"fprov": "QC", "sort": "D", |
| 101 | 106 | "fage": DAYS, |
| 102 | 107 | "page": str(page)}).text |
| 103 | − break | |
| 104 | 108 | except (requests.ConnectionError, requests.Timeout): |
| 105 | 109 | # le WAF du Guichet coupe parfois la connexion : une |
| 106 | 110 | # reprise après pause, sinon on garde l'acquis partiel |
| 107 | − if attempt == 2: | |
| 111 | + if attempt == 3: | |
| 108 | 112 | if out: |
| 109 | 113 | return out |
| 110 | 114 | raise |
| 111 | 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 | 126 | items = _ITEM_RE.findall(html) |
| 113 | 127 | if not items: |
| 114 | 128 | break |
| 115 | 129 | |