SPB Git forge

spb/fabri-ka

Public

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

217commits 1branches 0releases
66.1 MBsize
maindefault branch
7 h agolast push
Python 38.4% HTML 30.3% TypeScript 17.2% CSS 11% JavaScript 3.2%

[ka6] fix connecteur inspirationgourmande.ca: wp-json retourne 500 (erreur PHP serveur), pages HTML cachées via WP Super Cache — fallback HTML dans WooCommerceConnector: scrape accueil + catégories FR/EN (/categorie-produit/), extrait JSON-LD Product sur chaque page produit accessible

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Simon-Pierre Boucher committed 1 mo ago (Aug 23, 2026) parent e104a57

1 changed file +90 −1

modified fabrika/connectors/woocommerce.py +90 −1
@@ -6,6 +6,9 @@
6 6 # -----------------------------------------------------------------------------
7 7 from __future__ import annotations
8 8
9 +import re
10 +from urllib.parse import urlparse
11 +
9 12 from ..schema import Product
10 13 from .base import BaseConnector
11 14
@@ -44,11 +47,89 @@ class WooCommerceConnector(BaseConnector):
44 47 return scrapfly.scrapfly_json(url)
45 48 raise exc
46 49
50 + def _html_fallback(self) -> list[Product]:
51 + """Fallback HTML quand la Store API WooCommerce retourne 500 (erreur serveur).
52 +
53 + Découvre les URLs produit depuis l'accueil et les pages catégories WooCommerce,
54 + puis extrait le JSON-LD Product de chaque page. Utilisé quand /wp-json/ est
55 + cassé mais que les pages HTML sont servies depuis le cache (ex. WP Super Cache).
56 + """
57 + from .generic import extract_product
58 +
59 + host = urlparse(self.base).netloc
60 +
61 + # Accueil + slugs de boutique courants (FR d'abord)
62 + discovery = [self.base + "/"]
63 + for path in ("/shop/", "/boutique/", "/boutique-en-ligne/",
64 + "/produits/", "/products/"):
65 + discovery.append(self.base + path)
66 +
67 + product_urls: set[str] = set()
68 +
69 + for page_url in discovery:
70 + try:
71 + r = self.session.get(page_url, timeout=self.timeout)
72 + if r.status_code != 200:
73 + continue
74 + html = r.text
75 + # Produits WooCommerce FR (/produit/) et EN (/product/)
76 + found = set(re.findall(
77 + rf'href="(https?://{re.escape(host)}/(?:produit|product)/[^"#?]+)"',
78 + html))
79 + product_urls |= found
80 + # Catégories WooCommerce FR (/categorie-produit/) et EN (/product-category/)
81 + cat_links = set(re.findall(
82 + rf'href="(https?://{re.escape(host)}/(?:categorie-produit|product-category)/[^"#?]+)"',
83 + html))
84 + for cat_url in cat_links:
85 + try:
86 + r2 = self.session.get(cat_url, timeout=self.timeout)
87 + if r2.status_code == 200:
88 + product_urls |= set(re.findall(
89 + rf'href="(https?://{re.escape(host)}/(?:produit|product)/[^"#?]+)"',
90 + r2.text))
91 + except Exception:
92 + pass
93 + except Exception:
94 + pass
95 +
96 + out: list[Product] = []
97 + for url in product_urls:
98 + try:
99 + r = self.session.get(url, timeout=self.timeout)
100 + if r.status_code != 200:
101 + continue
102 + info = extract_product(url, r.text)
103 + if not info:
104 + continue
105 + slug = url.rstrip("/").split("/")[-1][:80]
106 + out.append(Product(
107 + store_id=self.store_id,
108 + external_id=slug or url,
109 + url=url,
110 + title=info["title"],
111 + description=info.get("description") or "",
112 + price=info["price"],
113 + price_max=info["price"],
114 + currency=info.get("currency") or "CAD",
115 + images=[info["image"]] if info.get("image") else [],
116 + available=info.get("available"),
117 + ))
118 + except Exception:
119 + pass
120 + return out
121 +
47 122 def fetch(self) -> list[Product]:
48 123 out: list[Product] = []
49 124 page = 1
125 + first_page_exc: Exception | None = None
50 126 while page <= self.max_pages:
51 − items = self._get_items(f"{self.base}/wp-json/wc/store/v1/products?per_page=100&page={page}")
127 + try:
128 + items = self._get_items(f"{self.base}/wp-json/wc/store/v1/products?per_page=100&page={page}")
129 + except Exception as exc:
130 + if page == 1:
131 + first_page_exc = exc
132 + break
52 133 if not isinstance(items, list) or not items:
53 134 break
54 135 for it in items:
@@ -125,4 +206,12 @@ class WooCommerceConnector(BaseConnector):
125 206 if len(items) < 100:
126 207 break
127 208 page += 1
209 +
210 + if first_page_exc is not None and not out:
211 + # Store API inaccessible dès la 1ʳᵉ page → fallback HTML + JSON-LD
212 + html_out = self._html_fallback()
213 + if html_out:
214 + return html_out
215 + raise first_page_exc
216 +
128 217 return out
129 218