# ----------------------------------------------------------------------------- # Auto-Ka — Agrégateur de voitures usagées à vendre (province de Québec) # Auteur : Simon-Pierre Boucher — contact@spboucher.ai # connectors/convertus_fc.py : concessionnaires WordPress « Convertus » # (thème achilles) protégés par Cloudflare — accès via Firecrawl. # # Même API VMS que clubautozone.py (proxy officiel du site # ajax-vehicles.php -> vms.prod.convertus.rocks), mais chaque appel passe # par Firecrawl pour franchir Cloudflare. Le JSON revient enveloppé dans # un HTML — on l'extrait puis on réutilise le parseur VMS. # ----------------------------------------------------------------------------- from __future__ import annotations import json import re import urllib.parse from ..schema import Vehicle from .clubautozone import ClubAutoZone _VMS_API = "https://vms.prod.convertus.rocks/api/" _PROXY = "/wp-content/plugins/convertus-vms/include/php/ajax-vehicles.php" class ConvertusFirecrawlConnector(ClubAutoZone): """Hérite du parseur VMS de ClubAutoZone ; fetch via Firecrawl.""" source_id = "" # base abstraite : non enregistrée inventory_id = 0 def _page(self, pg: int) -> dict: endpoint = (f"{_VMS_API}filtering/?cp={self.inventory_id}" f"&ln=fr&sc=used&pg={pg}&pc=100") url = (f"{self.base_url}{_PROXY}" f"?endpoint={urllib.parse.quote(endpoint, safe='')}" f"&action=vms_data") html = self.get_rendered(url) # le JSON est enveloppé dans … par le rendu Firecrawl m = re.search(r"]*>(.*)", html, re.S) payload = (m.group(1) if m else html).strip() try: return json.loads(payload) except ValueError: return {} def _resolve_inventory_id(self) -> None: """Lit globalVars.inventoryId sur la page inventaire si inconnu.""" if self.inventory_id: return html = self.get_rendered(f"{self.base_url}/vehicles/used/") m = re.search(r'inventoryId"\s*:\s*"?(\d+)', html) if not m: raise RuntimeError("inventoryId introuvable") self.inventory_id = int(m.group(1)) def fetch(self) -> list[Vehicle]: if not self.source_id: return [] self._resolve_inventory_id() return super().fetch() class BernieresAuto(ConvertusFirecrawlConnector): source_id = "bernieresauto" base_url = "https://www.bernieresauto.com" dealer_name = "Bernières Auto" city = "Saint-Nicolas" inventory_id = 1305 class AutoFlash(ConvertusFirecrawlConnector): source_id = "autoflash" base_url = "https://www.autoflash.net" dealer_name = "AutoFlash" city = "Saint-Hubert" inventory_id = 1306 class ComplexeAutoPlus(ConvertusFirecrawlConnector): source_id = "complexeautoplus" base_url = "https://www.complexeautoplus.com" dealer_name = "Complexe Auto Plus" city = "Saint-Jérôme" inventory_id = 855