SPB Git

spb/fabri-ka Public

Agrégateur de produits québécois — www.fabri-ka.com

HTML 57.9% Python 18.6% TypeScript 15.6% CSS 7.8%
2.5 KB · 67 lines python
Raw Blame History
1# -----------------------------------------------------------------------------2# Fabri-Ka — Agrégateur de produits québécois3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# connectors/base.py : connecteur générique par plateforme ecommerce.5#   Contrairement à Lou-Ka (1 connecteur / site), Fabri-Ka utilise6#   1 connecteur / PLATEFORME : le même code Shopify sert des centaines7#   de boutiques, piloté par le registre data/stores.json.8# -----------------------------------------------------------------------------9from __future__ import annotations1011import os12import time1314import requests1516from ..schema import Product1718USER_AGENT = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "19              "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126 Safari/537.36 "20              "FabriKaBot/1.0 (+https://www.fabri-ka.com/bot; contact@spboucher.ai)")2122FIRECRAWL_API = "https://api.firecrawl.dev/v1/scrape"232425class BaseConnector:26    """Connecteur de plateforme : instancié avec la fiche registre d'une boutique."""2728    platform: str = ""29    request_delay: float = 0.530    timeout: int = 3031    max_pages: int = 200          # garde-fou (200 pages x 250 = 50 000 produits)3233    def __init__(self, store: dict) -> None:34        self.store = store35        self.store_id = store["id"]36        self.base = (store.get("url") or f"https://{store['id']}").rstrip("/")37        self.session = requests.Session()38        self.session.headers["User-Agent"] = USER_AGENT39        self.session.headers["Accept-Language"] = "fr-CA,fr;q=0.9,en;q=0.8"40        self._last = 0.04142    def get(self, url: str, **kw) -> requests.Response:43        wait = self.request_delay - (time.time() - self._last)44        if wait > 0:45            time.sleep(wait)46        resp = self.session.get(url, timeout=self.timeout, **kw)47        self._last = time.time()48        resp.raise_for_status()49        return resp5051    def get_rendered(self, url: str) -> str:52        """HTML rendu (JavaScript exécuté) via Firecrawl — sites SPA/anti-bot."""53        key = os.environ.get("FIRECRAWL_API_KEY")54        if not key:55            raise RuntimeError("FIRECRAWL_API_KEY manquant (voir .env)")56        resp = requests.post(57            FIRECRAWL_API,58            json={"url": url, "formats": ["html"]},59            headers={"Authorization": f"Bearer {key}"},60            timeout=120,61        )62        resp.raise_for_status()63        return (resp.json().get("data") or {}).get("html", "")6465    def fetch(self) -> list[Product]:   # pragma: no cover - interface66        raise NotImplementedError67