Tweaker Skit3000 heeft een site ontwikkeld waarmee Nederlanders prijzen uit supermarkten kunnen vergelijken. Via checkjebon.nl kunnen gebruikers boodschappenlijstjes invoeren of een kassabonnetje inscannen. De site controleert bij welke supermarkt het lijstje het goedkoopst is.
Ontwikkelaar Sjoerd van der Hoorn, op Tweakers bekend als Skit3000, geeft op Github meer uitleg over de werking van checkjebon.nl. Zijn website maakt gebruik van tesseract-OCR-software om tekst te herkennen op kassabonnetjes waarna er ook nog onderliggende processing wordt gedaan om namen van producten te herkennen. De site probeert aan elke ingevoerde productnaam vervolgens een product te koppelen. Eerst wordt er gezocht naar exact dezelfde productnaam, maar als die niet wordt gevonden, zoekt het algoritme naar een bredere omschrijving. Het algoritme zoekt in dat geval naar productnamen waarin alle letters in dezelfde volgorde voorkomen als ze zijn opgegeven. Die letters hoeven niet aan elkaar geschreven te staan waardoor meerdere producten in aanmerking kunnen komen.
Als er een inhoudsmaat of gewicht wordt meegegeven, filtert het algoritme alle producten uit die minder dan de opgegeven waarde bevatten. Als er hierdoor geen resultaten te zien zijn, wordt het filter ongedaan gemaakt. De resultaten worden door het algoritme op kostprijs gerangschikt en enkel het goedkoopste artikel dat in een bepaalde supermarkt te vinden is, wordt vervolgens weergegeven als de vergelijkbare optie in een andere supermarkt.
Skit3000 gebruikt daarvoor Node-RED, zegt hij tegen Tweakers. "Dat is een handige tool om snel prototypes mee te ontwikkelen, omdat je allerlei verschillende blokken functionaliteit aan elkaar kunt plakken zonder dat je eerst zelf allerlei dependencies hoeft te downloaden. De visuele omgeving laat je makkelijk de output van nodes inspecteren en maakt Node-RED voor NodeJS eigenlijk wat Jupyter is voor Python. De Inject-node, waarmee je handmatig een flow kunt starten, kun je met een paar klikken ook gelijk opzetten zodat het op gezette tijden de flow start zonder dat je zelf bijvoorbeeld een cronjob hoeft aan te maken. Het is ook makkelijk om de connectie tussen twee nodes tijdelijk te verbreken, dus terwijl je aan het debuggen bent kun je voorkomen dat je bijvoorbeeld je database vervuilt met allemaal testdata. Voor het scrapen van prijzen is dit ideaal. Als een flow tijdens het scrapen ergens vastloopt kun je het vanuit één omgeving starten, inspecteren wat er misgaat, aanpassingen maken en testen en het daarna gelijk weer in productie toepassen."
"Alle scrapers beginnen met een Inject-node die dagelijks automatisch wordt getriggerd. Hierna komt een template-node met daarin JavaScript-code die uitgevoerd moet worden in een browser om te navigeren door de site van de betreffende supermarkt. Hierna komt een nbrowser-node die een headless browser start, naar de site van de supermarkt gaat en het script uitvoert."
"Terwijl dit bezig is, draait een subflow die elke tien seconden een variabele inspecteert die het scrape-script moet wijzigen zodra het klaar is en ondertussen het aantal producten dat tot dan toe is gescrapet, toont. Als er een fout is of de flow komt vast te zitten in een loop zonder nieuwe producten tegen te komen, dan sluit de subflow af via de error-output die ik heb aangemaakt en stuurt de main flow een mail aan mezelf zodat ik weet dat ik hier iets aan moet doen. Zijn er geen fouten en zijn alle pagina's gescrapet, dan wordt de subflow via de ok-output verlaten en worden de producten opgeslagen in een json-bestand."
"Tot slot, als alle scrapers klaar zijn, draait een flow die de verschillende json-bestanden samenvoegt en opschoont. Omdat Checkjebon alle data als één bestand inlaadt en het om meer dan honderdduizend producten gaat, verklein ik de attribuutnamen van 'price', naar 'p', etcetera. Ik dacht dat dit niet nodig was omdat alles via GitHub Pages wordt gehost en ze gzip-compressie toepassen, maar blijkbaar is het token dat gebruikt werd voor 'price' langer dan de acht bits die nodig zijn voor een enkele 'p'. Ik heb nog gekeken om verdere compressie toe te passen door van een json-object over te stappen naar een twee-dimensionale array, maar dat leverde maar een kleine besparing op. Het duurde ook langer om dat weer goed in te lezen in een json-object in de browser van de gebruiker."
Aangezien zowel de scrapers als de website gebruikmaken van JavaScript, zegt Skit3000 dat hij stukken code kan hergebruiken, zoals die voor het detecteren en omrekenen van hoeveelheden naar liters en kilogram. "Als ik een andere taal had gebruikt, had ik deze code twee keer moeten schrijven."
Checkjebon.nl kan momenteel het assortiment uit Albert Heijn-, Coop, Dirk, Hoogvliet, Jan Linders, Jumbo-, Plus-, Spar- en Vomar-supermarkten uitlezen. Er is beperkt ondersteuning voor het assortiment van Aldi- en DekaMarkt-supermarkten, omdat deze winkels niet alle prijzen van hun waren online delen.
De ontwikkelaar zegt dat alle data lokaal wordt verwerkt. De verwerking van gegevens gebeurt volgens hem in de browser en de resultaten daarvan worden niet bijgehouden op de website of doorverkocht aan adverteerders.