[ka2] fix connecteur gimcote: rotation d'IP résidentielle collante contre les IP Oxylabs flaguées par SiteGround — même durcissement que boreal_abitibi (763a8ca), qui notait déjà la flakiness gimcote. Le sync du 2026-09-14 06:42 a échoué franc (« challenge sgcaptcha non contourné » sur /property-type/appartement/) : l'unique IP résidentielle collante tirée était elle-même flaguée, et _get_html ne tentait qu'UNE IP à cause du guard `if not self.session.proxies` (fix 12df764) — ~1 run sur 2 selon l'humeur du filtre SiteGround, le passage ka2 du 2026-09-13 19:56 avait diagnostiqué « échec transitoire » mais la récurrence prouve le trou. Vérifié live : direct → 202 sg-captcha: challenge ; session collante fraîche → 200, 75 Ko, cartes présentes. Fix minimal, pattern boreal : _get_html tire jusqu'à 2 nouvelles sessions collantes (sessid en time_ns, fini les collisions de int(time.time())) avant l'erreur franche ; le chemin direct reste tenté d'abord (gratuit si SiteGround déflague l'IP du nœud) ; un éventuel 403 passe déjà par l'escalade du wrapper résilient. Testé : fetch() venv x3 → 79 annonces chaque run (3½ Ste-Foy semi-meublé 995 $/mois, 14 photos — médiane 79.0 pile), pytest fixtures 2 passed, run.py sync gimcote → found=79 ok (14,9 s), pm2 restart lou-ka-sync, site :8095 → 200.
1 changed file +13 −3
modified
louka/connectors/gimcote.py
+13 −3
@@ -12,6 +12,10 @@ | ||
| 12 | 12 | # (2xx) ; on bascule alors la session sur un proxy résidentiel Oxylabs CA |
| 13 | 13 | # (même pattern que boreal_abitibi/citiluxx/cromwell/deschenes_pepin/ |
| 14 | 14 | # gestion_habitation, hébergeur identique). |
| 15 | +# 2026-09-14 : l'IP résidentielle collante tirée peut elle-même être flaguée | |
| 16 | +# (challenge ou 403 SiteGround, ~1 run sur 2) ; on tourne donc l'IP | |
| 17 | +# (jusqu'à 2 nouvelles sessions collantes) avant l'erreur franche — même | |
| 18 | +# durcissement que boreal_abitibi. | |
| 15 | 19 | # ----------------------------------------------------------------------------- |
| 16 | 20 | from __future__ import annotations |
| 17 | 21 | |
@@ -91,18 +95,24 @@ class GimCoteConnector(BaseConnector): | ||
| 91 | 95 | pwd = os.environ.get("OXYLABS_PROXY_PASS") |
| 92 | 96 | if not (endpoint and user and pwd): |
| 93 | 97 | return False |
| 94 | − puser = f"{user}-cc-CA-sessid-gimcote{int(time.time())}-sesstime-10" | |
| 98 | + puser = f"{user}-cc-CA-sessid-gimcote{time.time_ns()}-sesstime-10" | |
| 95 | 99 | proxy = f"http://{quote(puser, safe='')}:{quote(pwd, safe='')}@{endpoint}" |
| 96 | 100 | self.session.proxies = {"http": proxy, "https": proxy} |
| 97 | 101 | self.session.verify = False # CA MITM du proxy Oxylabs (cf. _resilient) |
| 98 | 102 | return True |
| 99 | 103 | |
| 100 | 104 | def _get_html(self, url: str) -> str: |
| 101 | − """GET avec contournement du challenge sgcaptcha (proxy résidentiel).""" | |
| 105 | + """GET avec contournement du challenge sgcaptcha (proxy résidentiel). | |
| 106 | + | |
| 107 | + Une IP résidentielle collante peut elle-même être flaguée : on tire | |
| 108 | + jusqu'à 2 nouvelles sessions collantes avant l'erreur franche. | |
| 109 | + """ | |
| 102 | 110 | resp = self.get(url) |
| 103 | 111 | if not _sg_challenge(resp): |
| 104 | 112 | return resp.text |
| 105 | − if not self.session.proxies and self._enable_residential_proxy(): | |
| 113 | + for _ in range(2): | |
| 114 | + if not self._enable_residential_proxy(): | |
| 115 | + break | |
| 106 | 116 | resp = self.get(url) |
| 107 | 117 | if not _sg_challenge(resp): |
| 108 | 118 | return resp.text |
| 109 | 119 | |