Skip to main content
Glama

chineur

Chiffrage d'un projet entier au meilleur prix livraison comprise, neuf et occasion séparés, avec un plan de commande groupé qui minimise le total réellement payé.

Le moins cher article par article n'est pas la bonne réponse : 10 articles chez 10 marchands, c'est 10 frais de port. Le moteur de panier groupe les achats chez 2 ou 3 marchands, seuils de franco compris, et affiche l'écart avec ce total naïf.

Installation

python -m venv .venv && ./.venv/bin/pip install -e .
./.venv/bin/pytest          # 74 tests hors ligne, aucun réseau

Python 3.11 ou plus. Le projet tourne sans aucune clé : LeDénicheur, AliExpress et Leboncoin ne demandent rien. Les sources à clé (eBay, Mouser) restent simplement éteintes, et list_sources() le dit.

Related MCP server: aws-calculator-assistant

Clés

Aucun secret n'est lu dans un fichier versionné. bin/chineur-mcp.sh charge un .env local s'il existe (ignoré par git), sinon il prend l'environnement tel quel, ce qui permet de les injecter depuis un gestionnaire de secrets par simple substitution, la valeur ne transitant alors que par l'environnement du processus :

export EBAY_CERT_ID=$(votre-cli-de-coffre get password 'eBay - API Browse')

Variable

Rôle

Sans elle

EBAY_APP_ID / EBAY_CERT_ID

keyset eBay Browse

source eBay éteinte

MOUSER_API_KEY

clé Mouser Search API

source Mouser éteinte

FLARESOLVERR_URL

recours navigateur sur mur anti-bot

recours éteint, le reste fonctionne

CHINEUR_VAR

répertoire d'état (cache, quotas, cookies, journal)

<dépôt>/var

Utilisation

Exposé en MCP stdio (bin/chineur-mcp.sh), trois outils :

Outil

Rôle

price_project(articles, zipcode?, used_threshold?, detail?)

chiffre une liste complète en un seul appel

best_price(article, …)

le cas à un seul article, en detail="full"

list_sources()

état, quota horaire restant et coupure en cours de chaque source

Chaque article accepte une quantité ou un plafond : "LD2410C x3", "ESP32-S3 <=12€".

En ligne de commande :

python -c "
import asyncio; from chineur.models import Project
from chineur.search import Orchestrator; from chineur.report import render
async def m():
    o = Orchestrator(); print(render(await o.run(Project.build(['LD2410C x2','BME280'])), failures=o.failures))
asyncio.run(m())"

Sources et ce qu'elles valent

Source

Volet

Port

Remarque

LeDénicheur

neuf + occasion

exact

agrégateur : apporte Amazon, Cdiscount, Fnac, Rue du Commerce, Back Market, Rakuten en une requête

AliExpress

neuf

estimé

seule source réaliste pour les modules chinois de niche

Leboncoin

occasion

estimé

10 recherches/heure maximum, non négociable

eBay

neuf + occasion

exact

API Browse officielle, active depuis le 2026-08-06

Mouser

neuf

estimé

distributeur pro, API officielle. Couvre ce qu'AliExpress rate (étain, tresse, résistances, transistors, connectique). Inutile sur les modules chinois : absent, ou 3 à 5x le prix

Limites connues, mesurées le 2026-08-05 et non contournables sans Playwright (V2) :

  • AliExpress ne donne ni le vendeur ni le port sur la page de recherche, et la fiche produit en HTTP simple est vide. Le groupage se fait donc au niveau de la plateforme, avec son seuil de franco. Après plusieurs requêtes rapprochées, AliExpress répond 200 avec une page de 2 Ko : c'est détecté et traité comme un blocage, pas comme une absence d'offre.

  • Le budget AliExpress est d'environ 6 requêtes par IP fraîche, mesuré le 2026-08-10 après 46 h sans aucune sollicitation : 6 articles servis (133 offres), blocage au 7e, FlareSolverr mis au mur lui aussi. Une liste de 17 articles ne peut donc pas être chiffrée en une fois par scraping, quelle que soit l'attente respectée. C'est la limite structurelle qui justifie l'API affiliée officielle (portals.aliexpress.com), et non un réglage de patience à trouver.

  • En occasion, le prix est indicatif. Les annonces Leboncoin sont du texte libre : un résultat peut être un lot, une pièce cassée ou un accessoire. Neuf et occasion ne sont jamais fusionnés en un prix unique.

  • Le port est marqué ~ quand il est estimé (table shipping.py) et ? quand il est inconnu. Un total contenant une estimation est signalé comme tel.

Pertinence : le vrai facteur limitant

Mesuré le 2026-08-08 sur la liste type réelle (17 articles, composants) : les « introuvables » ne venaient pas d'un manque de sources mais du libellé. Deux causes distinctes, séparées en comptant les titres bruts rendus par eBay avant filtrage.

La requête est trop longue. « etain soudure 60/40 flux 0.8mm » fait renvoyer 0 résultat par eBay ; « etain soudure » en renvoie 11. Chaque mot descriptif ajouté rétrécit le résultat jusqu'au vide. D'où reduced_query et la seconde chance de guarded_search : quand une source ne rend rien, on retente une fois sur un libellé réduit aux références et aux deux premiers mots porteurs, les qualifiants jetés. Une requête de plus uniquement en cas d'échec, et jamais sur le dernier crédit du quota.

Le filtre jetait du bon. « cables dupont assortiment » contre « Câble Dupont … Assortiment 5 à 100pcs » tombait à 0.67 pour un seuil de 0.70 : le score ne comparait que par inclusion de chaîne, donc le pluriel cassait tout. _apparie rapproche désormais sur le préfixe commun (60 % du mot), réservé aux mots d'au moins 4 lettres — jamais aux nombres ni aux références courtes, pour que « 2410 » et « 2420 » restent deux capteurs différents.

Résultat sur la même liste : eBay passe de 10 à 14 articles couverts sur 17, articles sans aucune offre de 5 à 3. Restent trois vrais introuvables, à toutes les sources : la plaque pastillée, le Diese 2201M et la Sugon 8620DX.

Le prix à payer, et il est réel. Une recherche élargie répond à une question plus large que celle posée : « ESP32-WROOM-32 DevKit 38 broches » élargi en « esp32 wroom 32 » remonte un modèle 30 broches. Toute offre issue d'un élargissement porte donc la réserve « recherche élargie à « … » », qui doit rester visible jusqu'à l'affichage.

Recours navigateur (FlareSolverr)

Le point faible du projet : AliExpress est la seule source des modules chinois, et elle se fait bloquer au bout de 5 à 9 recherches. Le README annonçait ce trou comme « non contournable sans Playwright (V2) ». Il l'est, sans dépendance supplémentaire côté projet : une instance FlareSolverr pilote un vrai Chrome, exécute le JavaScript et porte une empreinte de navigateur complète. Elle se déclare par FLARESOLVERR_URL ; non renseignée, le recours est simplement éteint.

Mesuré le 2026-08-08, alors que le connecteur direct était bloqué et son disjoncteur ouvert : fr.aliexpress.com/w/wholesale-LD2410C.html rend HTTP 200, 832 Ko, aucun marqueur anti-bot, et le parseur HTML existant y décode 60 articles avec prix en EUR. Aucune modification du parsing n'a été nécessaire.

Branchement dans AliExpress._items : sur mur anti-bot ou page tronquée, on tente le recours avant de lever Blocked. Points de conception :

  • Recours, pas régime. Chaque appel démarre un Chrome : c'est lent et lourd. On n'y passe que sur un blocage avéré.

  • None et pas [] quand le recours ne donne rien. Une liste vide passerait pour « aucun résultat », le disjoncteur ne s'ouvrirait pas, et le connecteur retaperait la source bloquée à l'article suivant en aggravant le bridage.

  • Jamais bloquant. flaresolverr.fetch ne lève pas : une panne du solveur masquerait la vraie cause derrière un incident d'infrastructure. FLARESOLVERR_URL= l'éteint.

  • Abandon après 2 échecs consécutifs. Voir la limite ci-dessous : sans ça, chaque article suivant paierait encore des dizaines de secondes d'attente pour rien.

Sa limite, mesurée le 2026-08-08. Le recours n'est pas un contournement, c'est un second budget. Sur huit recherches AliExpress enchaînées : six servies à 60 articles chacune en 4 à 6 s, la septième en erreur 500 après 101 s d'attente, la huitième renvoyant le mur anti-bot (208 Ko, marqueur RGV587). L'IP de sortie ne change pas, donc le même mur finit par tomber. Deux conséquences dans le code : maxTimeout ramené de 90 à 45 s, et un mur rendu en HTTP 200 compte comme un échec, sinon le compteur d'abandon ne se déclencherait jamais.

En pratique le budget AliExpress passe donc d'environ 9 recherches à environ 15, avec 60 articles par page au lieu de 20. C'est un gain net, ce n'est pas l'illimité.

Garde-fous anti-ban

L'IP de sortie est unique et résidentielle : un ban la coupe entièrement. Tout le module est écrit pour ne jamais en arriver là.

  • Quotas horaires persistés en SQLite (var/chineur.db) : Leboncoin 10/h, LeDénicheur 30/h, AliExpress 20/h, eBay 200/h, Mouser 60/h (quotas API, aucun risque IP).

  • Espacement sérialisé par source, pas un simple sleep : l'orchestrateur lançant tous les articles en parallèle, des délais concurrents s'écouleraient en même temps et les requêtes partiraient quand même en rafale. Chaque départ attend le précédent.

  • Disjoncteur sur 403 / captcha / page tronquée : 1 h, 2 h pour AliExpress, dont la pénalité persiste au-delà de la rafale qui l'a déclenchée.

  • Le blocage AliExpress n'est ni un quota ni une question de cadence. Trois recettes du 2026-08-06, libellés jamais mis en cache :

    espacement

    transport

    servies avant blocage

    6-12 s

    requêtes nues

    5 (blocage à 46 s)

    6-12 s

    session à cookies

    6 (blocage à 56 s)

    45-60 s

    session à cookies

    2 (blocage à 108 s)

    2,5 s

    session à cookies, endpoint JSON

    9 (blocage à 30 s)

    Ralentir dégrade le rendement, donc la cadence n'est pas le levier : le meilleur rendement s'obtient en allant vite. Le blocage dure ~40 min, mesuré deux fois, et se signale par FAIL_SYS_USER_VALIDATE / RGV587_ERROR::SM, l'anti-bot d'Alibaba qui exige une validation.

  • Les articles sont lus sur l'endpoint interne POST www.aliexpress.com/fn/search-pc/index (celui que la page de recherche appelle elle-même), charge utile de forme mods, depuis une session déjà ouverte sur l'accueil. Il rend exactement les mêmes objets article que la page HTML, en .data.result.mods.itemList.content, pour 390 Ko au lieu de 600. Piège : plusieurs blocs de la réponse portent une clé content, celui des filtres de recherche arrive avant itemList. La page HTML reste en repli automatique, mais uniquement si la réponse n'a plus la forme attendue : sur un mur anti-bot, retenter en HTML depuis la même IP ne ferait que gaspiller une requête.

  • Session persistante (transport.Session) conservée pour AliExpress : page d'accueil visitée une fois, cookies rejoués ensuite, pot dans var/cookies/ pour survivre à un redémarrage, jeté dès qu'une page revient tronquée. Aucun gain mesuré sur le blocage, gardée parce qu'elle rapproche le trafic de celui d'un navigateur.

  • Piste non tranchée : si l'endpoint interne se referme un jour, sous-traiter la source à un service hébergé (omkar.cloud, 5 000 requêtes/mois gratuites), au prix d'un tiers qui voit nos recherches.

  • Cache consulté avant le rate limiter : rechiffrer un projet ne retape rien. TTL 1 h par défaut, mais jamais plus court que la coupure du disjoncteur (Connector.effective_ttl, 6 h pour AliExpress). Sans ce plancher, un projet plus large que le budget d'une source ne peut pas se terminer : la relance d'après-coupure retrouve un cache vide, refait les mêmes premières recherches, rebrûle le budget et se fait rebloquer, indéfiniment. Sauf eBay, exclu du cache pour rester conforme à l'exemption (voir plus bas).

  • Un projet dépassant le quota Leboncoin est traité par ordre d'enjeu décroissant, et les articles non traités sont nommés dans la restitution.

Économie de tokens

Le calcul reste en Python, seul le verdict remonte : sortie en texte tabulaire dense, une meilleure offre neuve et une occasion par article, titres tronqués, URL nettoyées, notes regroupées, diagnostics dans var/chineur.log. Mesuré : 10 articles ≈ 650 tokens.

Tests

pytest              # 74 tests hors ligne, aucun réseau
pytest -m network   # 5 tests d'intégration réels (consomment du quota)

Le moteur de panier est entièrement testable hors ligne, y compris ses cas piégeux (seuil de franco déclenché par le groupage, marchand unique, article sans offre, égalités). La propriété « total optimisé ≤ total naïf » est vérifiée sur 50 jeux d'offres aléatoires.

Mouser

Une seule variable : MOUSER_API_KEY. Clé gratuite en self-service sur mouser.com/api-hub (onglet Search API).

La clé doit être créée depuis mouser.fr. La recherche par mot-clé n'accepte aucun paramètre de devise (vérifié dans le swagger api.mouser.com/api/docs/V1) : la devise est celle du site d'inscription. Une clé créée sur mouser.com renvoie des dollars, que le connecteur écarte plutôt que de les convertir à un taux inventé — la source paraîtrait simplement vide.

Quotas annoncés par Mouser : 50 pièces par appel, 30 appels/minute, 1 000 appels/jour. D'où limit_per_hour = 60 et 2 s d'espacement : ce n'est pas de l'anti-ban, c'est le respect du quota.

Trois choix de fond :

  • mouserPaysCustomsAndDuties: true — Mouser expédie du Texas. Sans ce drapeau, les droits de douane arrivent après coup et un prix « moins cher » sur le papier devient plus cher à la livraison.

  • searchOptions: InStock — une référence à 12 semaines de délai n'a rien à faire dans une comparaison de prix pour un projet en cours.

  • Paliers de prix suivis à la quantité demandée. Retenir le palier 1 surestime tout achat groupé de composants passifs.

Les deux pièges qui fausseraient une comparaison, tous deux signalés en réserve sur l'offre : le minimum de commande (0,10 € l'unité par sachet de 100, soit 10 € réels à la caisse) et le format de prix (« 0,096 € » est un prix à trois décimales, « $1,250 » vaut mille deux cent cinquante — un parseur naïf se trompe d'un facteur mille dans les deux sens).

Port : non vérifié. Le franco de 50 € HT et les ~20 € en dessous sont recoupés sur le web (2026-08-07), pas sur une facture. Ils vivent donc dans shipping.py et ressortent en ESTIMATED, jamais en fait établi. À corriger dès la première commande réelle : sous le franco, le port écrase le gain, et c'est ce seuil qui décide si la source sert à quelque chose.

eBay

EBAY_APP_ID (App ID) et EBAY_CERT_ID (Cert ID), pour un keyset Production — le seul exploitable. Un keyset Sandbox se branche avec EBAY_ENV=sandbox, qui bascule sur api.sandbox.ebay.com. En sandbox le connecteur reste éteint : l'inventaire est factice, les prix entreraient dans le panier sans que rien ne le signale. EBAY_SANDBOX_OK=1 le rallume pour tester la plomberie uniquement.

Vérifié le 2026-08-06 sur un keyset Production : jeton client_credentials obtenu, recherches réelles (29 offres sur « cable hdmi 2.1 2m », port exact lu dans shippingOptions).

L'état de l'article se lit sur conditionId, jamais sur condition : ce dernier est traduit par eBay (« Neuf », « Occasion », « Ouvert (jamais utilisé) » sur EBAY_FR), et la table à clés anglaises d'origine ne matchait rien, donc tout ressortait en occasion. Les articles 7000 (« pour pièces ») sont écartés : à 3 €, un article cassé gagnerait la comparaison sans que rien ne le signale.

Conformité (« Your Keyset is currently disabled »). eBay n'active un keyset Production qu'après abonnement aux notifications de suppression de compte, ou exemption. Le projet prend l'exemption « je ne conserve pas de données eBay », et le code la rend littéralement vraie : EBay.cacheable = False, donc aucune réponse eBay n'est écrite dans var/chineur.db (elles contiennent des pseudos vendeurs). Un test le verrouille. Si un jour on remet eBay en cache, l'exemption devient fausse : il faudra héberger l'endpoint de notification à la place.

Licence

MIT, voir LICENSE.

Install Server
A
license - permissive license
A
quality
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    B
    quality
    B
    maintenance
    MCP server for finding, comparing, and ranking the cheapest real offers across eBay, Amazon, Craigslist, OfferUp, and Google Shopping, with tax estimation and exact-model filtering.
    6
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Build shareable AWS Pricing Calculator estimates programmatically via MCP tools, designed for creating MAP funding estimates from customer infrastructure descriptions.
    8
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    A hosted MCP server that routes natural-language shopping queries to independent merchant storefronts, returning normalized product and merchant results for AI agents and shopping assistants.
    MIT

View all related MCP servers

Related MCP Connectors

  • Hosted no-auth MCP for exact-spec packaging search, live price, stock, cart handoff, and no-match.

  • Real, buyable product and service offers — live pricing and trackable links for any MCP agent.

  • Routes natural-language shopping queries to merchant storefronts, returns normalized results.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/GRSMael/chineur'

If you have feedback or need assistance with the MCP directory API, please join our Discord server