From e2ee4dd30ecf579708cab435a8468157eb15fcb7 Mon Sep 17 00:00:00 2001
From: Valentijn van der Jagt <1119935@hr.nl>
Date: Sat, 22 Nov 2025 15:00:09 +0100
Subject: [PATCH] it all seems to work somewhat well
---
pom.xml | 5 ++
.../appie_scraper/BonusItem.java | 27 +++++-
.../appie_scraper/BonusManager.java | 86 +++++++++++++++++++
.../nl/herpiederpiee/appie_scraper/Main.java | 44 +++-------
.../nl/herpiederpiee/appie_scraper/Pair.java | 17 ++++
5 files changed, 148 insertions(+), 31 deletions(-)
create mode 100644 src/main/java/nl/herpiederpiee/appie_scraper/BonusManager.java
create mode 100644 src/main/java/nl/herpiederpiee/appie_scraper/Pair.java
diff --git a/pom.xml b/pom.xml
index c3b03be..7101d15 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,6 +22,11 @@
json
20250517
+
+ xyz.nextn
+ simple-levenshtein-distance
+ 1.0.0
+
diff --git a/src/main/java/nl/herpiederpiee/appie_scraper/BonusItem.java b/src/main/java/nl/herpiederpiee/appie_scraper/BonusItem.java
index 1f142b3..4fbc400 100644
--- a/src/main/java/nl/herpiederpiee/appie_scraper/BonusItem.java
+++ b/src/main/java/nl/herpiederpiee/appie_scraper/BonusItem.java
@@ -4,12 +4,24 @@ import com.microsoft.playwright.ElementHandle;
public class BonusItem {
String title;
+ String description = "";
String bonusText;
String category;
String imageURL;
+ String moreInfoURL;
+
+ float originalPrice = 0.0f;
+ float bonusPrice = 0.0f;
+
BonusItem(ElementHandle element) {
this.title = element.getAttribute("title");
+ this.moreInfoURL = "https://www.ah.nl"+element.getAttribute("href");
+
+ ElementHandle descriptionElement = element.querySelector("[data-testhook=\"card-description\"]");
+ if (descriptionElement != null) {
+ this.description = descriptionElement.innerText();
+ }
ElementHandle bonusElement = element.querySelector(".promotion-label-base_textContainer__DFx6D");
this.bonusText = bonusElement.innerHTML().replaceAll("<[^>]*>", " ");
@@ -17,7 +29,20 @@ public class BonusItem {
ElementHandle categoryContainer = element.evaluateHandle("el => el.closest('section')").asElement();
this.category = categoryContainer.getAttribute("id");
-
this.imageURL = element.querySelector(".promotion-card-image_img__Ca5n8").getAttribute("data-src");
+
+ ElementHandle priceContainer = element.querySelector("[data-testhook=\"price\"]");
+ if (priceContainer != null) {
+ float priceInteger = Float.parseFloat(priceContainer.querySelector(".promotion-price_integer__Tq2rf").innerText());
+
+ int priceDecimalsTemp = Integer.parseInt(priceContainer.querySelector(".promotion-price_fractional__U-irD").innerText());
+ float priceDecimals = (float) priceDecimalsTemp / 100;
+
+ this.bonusPrice = priceInteger + priceDecimals;
+ ElementHandle originalPriceElement = priceContainer.querySelector(".promotion-price_was__jhW9R");
+ if (originalPriceElement != null) { // those annoying "Christmas only items" are so annoying
+ this.originalPrice = Float.parseFloat(originalPriceElement.innerText());
+ }
+ }
}
}
diff --git a/src/main/java/nl/herpiederpiee/appie_scraper/BonusManager.java b/src/main/java/nl/herpiederpiee/appie_scraper/BonusManager.java
new file mode 100644
index 0000000..af0a38b
--- /dev/null
+++ b/src/main/java/nl/herpiederpiee/appie_scraper/BonusManager.java
@@ -0,0 +1,86 @@
+package nl.herpiederpiee.appie_scraper;
+
+import com.microsoft.playwright.*;
+import xyz.nextn.levenshteindistance.LevenshteinDistance;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.concurrent.TimeUnit;
+
+public class BonusManager {
+ ArrayList bonusItems = new ArrayList();;
+
+ public void updateBonusItems(){
+ try (Playwright playwright = Playwright.create()) {
+
+ Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false));
+
+ BrowserContext context = browser.newContext();
+ Page bonusPagina = context.newPage();
+
+
+ bonusPagina.navigate("https://www.ah.nl/bonus");
+ TimeUnit.SECONDS.sleep(5); // wait for page to actaully fully load
+
+ Locator bonusElements = bonusPagina.locator(".promotion-card_root__tQA3z");
+ for (ElementHandle bonusElement : bonusElements.elementHandles()){
+ BonusItem bonusItem = new BonusItem(bonusElement);
+
+ // exclude annoying elements
+ if (bonusItem.category.equals( "onlineOnly")) continue;
+ if (bonusItem.category.equals( "gall")) continue;
+ if (bonusItem.category.equals( "gall-card")) continue;
+ if (bonusItem.category.equals( "etos")) continue;
+
+ this.bonusItems.add(bonusItem);
+ }
+
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public ArrayList getBonusItems(String name){
+ ArrayList> list = new ArrayList<>();
+
+ for (BonusItem bonusItem : bonusItems) {
+ Integer score = fuzzyMatchScore(name, bonusItem.title);
+ list.add(Pair.pair(bonusItem, score));
+ }
+ list.sort((a, b) -> Integer.compare(b.second, a.second));
+
+ ArrayList top10 = new ArrayList<>();
+ int i = 0;
+ while (top10.size() < 10) {
+ top10.add(list.get(i).first);
+ i++;
+ }
+
+ return top10;
+ }
+
+ public int fuzzyMatchScore(String query, String title) {
+ query = query.toLowerCase();
+ title = title.toLowerCase();
+
+ if (title.contains(query)) {
+ return 100; // perfect match
+ }
+
+ int best = Integer.MAX_VALUE;
+
+ int qlen = query.length();
+ int tlen = title.length();
+
+ for (int i = 0; i <= tlen - qlen; i++) {
+ String sub = title.substring(i, i + qlen);
+ int dist = LevenshteinDistance.calculate(query, sub);
+ if (dist < best) best = dist;
+ }
+
+ // Convert distance to similarity percentage
+ int score = (int)(100.0 * (1.0 - (best / (double) qlen)));
+
+ return Math.max(0, Math.min(100, score));
+ }
+}
diff --git a/src/main/java/nl/herpiederpiee/appie_scraper/Main.java b/src/main/java/nl/herpiederpiee/appie_scraper/Main.java
index dc733f9..b659fdc 100644
--- a/src/main/java/nl/herpiederpiee/appie_scraper/Main.java
+++ b/src/main/java/nl/herpiederpiee/appie_scraper/Main.java
@@ -1,46 +1,30 @@
package nl.herpiederpiee.appie_scraper;
-import com.microsoft.playwright.*;
-import com.microsoft.playwright.options.WaitUntilState;
-import org.json.*;
+import com.microsoft.playwright.*;
+import org.json.*;
import java.util.ArrayList;
-import java.util.Random;
-import java.util.concurrent.TimeUnit;
+import java.util.Scanner;
public class Main {
public static void main(String[] args) {
- try (Playwright playwright = Playwright.create()) {
+ BonusManager bonusManager = new BonusManager();
+ bonusManager.updateBonusItems();
- Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false));
-
- BrowserContext context = browser.newContext();
- Page bonusPagina = context.newPage();
+ Scanner input = new Scanner(System.in);
- bonusPagina.navigate("https://www.ah.nl/bonus");
- System.out.println("Dom Content Loaded!");
- TimeUnit.SECONDS.sleep(5);
- ArrayList bonusItems = new ArrayList();
- int counter = 0;
- Locator bonusElements = bonusPagina.locator(".promotion-card_root__tQA3z");
- for (ElementHandle bonusElement : bonusElements.elementHandles()){
- BonusItem bonusItem = new BonusItem(bonusElement);
- bonusItems.add(bonusItem);
- counter++;
+ while (true) {
+ System.out.println("\n\nWhat item would you like to look for?");
+ String userInput = input.nextLine();
+ if (userInput.equals("qqq")) break;
+ ArrayList userResults = bonusManager.getBonusItems(userInput);
+
+ for (BonusItem bonusItem : userResults) {
+ System.out.println(bonusItem.title + " => " + bonusItem.bonusText);
}
- System.out.println("Amount of items: " + counter);
- // get random item from array
- Random random = new Random();
- BonusItem chosenItem = bonusItems.get(random.nextInt(bonusItems.size()));
- System.out.println("Random Item:\n"+chosenItem.title+" => "+chosenItem.bonusText + " ("+chosenItem.category+")\nImage URL:"+chosenItem.imageURL);
-
-
-
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
}
}
}
diff --git a/src/main/java/nl/herpiederpiee/appie_scraper/Pair.java b/src/main/java/nl/herpiederpiee/appie_scraper/Pair.java
new file mode 100644
index 0000000..c3b17a3
--- /dev/null
+++ b/src/main/java/nl/herpiederpiee/appie_scraper/Pair.java
@@ -0,0 +1,17 @@
+package nl.herpiederpiee.appie_scraper;
+
+/* generated by chatGPT, since i couldnt find a proper library that works how i want */
+public class Pair {
+
+ public F first;
+ public S second;
+
+ public Pair(F first, S second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ public static Pair pair(F first, S second) {
+ return new Pair<>(first, second);
+ }
+}
\ No newline at end of file