Bot de chasse aux bonnes affaires pour Vinted · interroge une API privée non documentée toutes les 5 s derrière des proxies gratuits rotatifs · alerte Discord quelques secondes après la mise en ligne d'une annonce
Architecture
Toutes les 5 s, le Deal poller tire un proxy aléatoire du pool que le Proxy refresher reconstruit en continu depuis ProxyScrape, puis exécute les 15 recherches de la Watchlist via le patched vinted_scraper contre le /api/v2/catalog/items de Vinted. Les résultats sont comparés à tout ce qui a déjà été vu, indexé par id d'article + channel ; chaque annonce réellement nouvelle donne lieu à un appel supplémentaire /items/{id} pour la réputation du vendeur et les photos, puis atterrit en POST /article sur la Discord bot API hébergée sur VPS, qui met en forme l'embed et notifie le channel correspondant au bucket de prix.
Déploiement : Poller → processus Python longue durée · machine locale · Discord bot API → VPS · connexion Discord toujours active
Résultats
- 5 s · cadence d'interrogation sur 15 recherches sauvegardées
- 120 s · rafraîchissement automatique du pool de proxies, zéro redémarrage manuel
- 0 € · coût des proxies : proxies gratuits rotatifs, aucun pool payant
- 15 → 11 · recherches routées vers des channels par bucket de prix
Compétences démontrées : rétro-ingénierie d'API privée · patch de dépendance (proxies, retry d'auth) · rotation de proxies sans infra payante · dégradation gracieuse plutôt que crash immédiat · conception de déduplication par identité · séparation producteur / consommateur des services
Problèmes et solutions
Chaque choix répond à une contrainte : Vinted n'a pas d'API publique et limite le débit des pollers agressifs, mais les bonnes affaires sous-cotées se vendent en quelques minutes.
▤ Parler l'API privée, pas le HTML
Problème : Les articles sous-cotés se vendent en quelques minutes, donc les alertes doivent arriver en quelques secondes. Scraper le HTML rendu est lent, fragile face à chaque changement de balisage, et facile à repérer pour le site.
Rétro-ingénierie de l'API JSON que l'application web Vinted appelle elle-même : rejouer un vrai cookie de session et un User-Agent de navigateur contre /api/v2/catalog/items, exactement comme le fait le frontend.
- Automatisation de navigateur (Selenium) rejetée : bien plus lourde, plus lente par interrogation, et cassée à chaque refonte
- Compromis accepté : une API non documentée peut changer sans préavis
- Gagné : interrogation toutes les 5 s de 15 recherches avec de petits payloads JSON au lieu de pages entières
▒ Proxies gratuits rotatifs avec un refresher en arrière-plan
Problème : Interroger toutes les 5 s depuis une seule IP domestique se fait rate-limiter ou bannir rapidement, et les proxies gratuits meurent constamment : une liste périmée bloquerait le bot en pleine exécution.
Chaque cycle choisit un proxy aléatoire dans un pool partagé qu'un thread daemon reconstruit depuis ProxyScrape toutes les 120 s, filtré sur les sorties françaises pour correspondre à la boutique.
- Proxies résidentiels payants rejetés : le coût récurrent est excessif à l'échelle d'un projet hobby
- Compromis accepté : les proxies gratuits sont lents et instables, donc chaque échec doit être survivable
- Gagné : coût d'infra nul et aucune intervention manuelle quand les proxies meurent
▓ Patcher la librairie, ne pas réécrire le client
Problème : Le vinted_scraper clé en main n'avait aucun support des proxies et levait une RuntimeError sur toute réponse non-200, tuant toute la boucle sur un seul proxy instable.
Modification de deux fichiers de la librairie : propagation des proxies jusqu'aux requêtes, re-fetch automatique du cookie de session sur 401 avec retry, et retour de None en cas d'échec pour que l'appelant saute un mauvais cycle au lieu de mourir.
- Client requests from scratch rejeté : il ré-implémenterait la gestion des cookies et du User-Agent que la librairie fait déjà bien
- try/except à chaque point d'appel rejeté : cela disperse la politique d'erreur dans tout le code
- Gagné : un poller qui survit à la mort d'un proxy et à l'expiration du cookie sans redémarrage
▞ Dédupliquer par id d'article + channel, en mémoire
Problème : Les pages de résultats triées par nouveauté se chevauchent d'une interrogation à l'autre ; re-notifier la même annonce spamme les channels et habitue les membres à ignorer les alertes.
La première passe ne fait que baseliner ce qui existe déjà. Ensuite, chaque résultat est enveloppé avec son channel cible et comparé par une égalité personnalisée sur id d'article + channel ; seules les paires inédites alertent.
- Le channel fait partie de la clé volontairement : une même annonce peut légitimement alerter à la fois dans le bucket 50 € et le bucket 150 €
- Base de données rejetée : une simple liste en mémoire suffit pour un unique processus longue durée
- Compromis accepté : un redémarrage re-baseline silencieusement, aucune persistance entre les exécutions
░ Récupération en deux étapes : recherche légère, puis hydratation
Problème : Les résultats de recherche manquent de la réputation du vendeur et des photos qu'un embed utile nécessite, mais récupérer les détails complets pour chaque résultat multiplierait les appels API et le risque de ban.
Interroger avec des recherches légères de 5 articles par page ; seules les annonces confirmées nouvelles par la déduplication obtiennent un second appel /items/{id} pour les détails complets avant publication.
- Hydratation systématique rejetée : le régime permanent reste à 15 requêtes par cycle quel que soit le volume de résultats
- Les rafales de nouveaux articles sont rares, donc l'appel supplémentaire est l'exception, pas la règle
- Gagné : des alertes plus riches (étoiles, nombre d'avis, pays) sans augmenter le débit de requêtes de base
▚ Séparer le poller de la livraison Discord
Problème : Mélanger scraping et mise en forme Discord dans un seul processus couple un scraper instable et sujet au ban à l'uptime et à la logique de channels du bot.
Le poller ne fait que produire : il POST un payload JSON plat via POST /article vers un service bot hébergé sur un VPS qui gère les embeds, le routage des channels et les images de secours pour les annonces sans photo.
- Le VPS maintient la connexion Discord 24/7 : un crash ou redémarrage du scraper ne fait jamais tomber le bot
- Intégration de discord.py dans le poller rejetée : un processus, une responsabilité
- Compromis accepté : deux déploiements à faire tourner et un saut réseau entre eux