From 7fa3e4a545b278136074abffba40c5e5c7f2c277 Mon Sep 17 00:00:00 2001 From: michael1986 Date: Fri, 28 Nov 2025 14:43:58 +0100 Subject: [PATCH] start --- .idea/data_source_mapping.xml | 7 - .idea/vcs.xml | 1 + cache/page_cache.db | Bin 21209088 -> 23035904 bytes pom.xml | 5 + .../java/com/auction/AuctionParsingTest.java | 295 +++++------ src/test/java/com/auction/Parser.java | 64 +++ .../com/auction/TroostwijkScraperTest.java | 22 - src/test/resources/test.html | 210 +------- src/test/resources/test.md | 456 ++++++++++++++++++ troostwijk.db | Bin 20480 -> 32768 bytes 10 files changed, 664 insertions(+), 396 deletions(-) delete mode 100644 .idea/data_source_mapping.xml create mode 100644 src/test/java/com/auction/Parser.java create mode 100644 src/test/resources/test.md diff --git a/.idea/data_source_mapping.xml b/.idea/data_source_mapping.xml deleted file mode 100644 index 68185f7..0000000 --- a/.idea/data_source_mapping.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..e6991cb 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/cache/page_cache.db b/cache/page_cache.db index 1b423a30b4aae85ec7070869934f2a884e96431e..fdf89f35949e4662630bd85c7b830b9ef48024db 100644 GIT binary patch delta 36018 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 diff --git a/pom.xml b/pom.xml index 372a40b..3b9d5c1 100644 --- a/pom.xml +++ b/pom.xml @@ -78,6 +78,11 @@ 5.10.1 test + + com.vladsch.flexmark + flexmark-all + 0.64.8 + diff --git a/src/test/java/com/auction/AuctionParsingTest.java b/src/test/java/com/auction/AuctionParsingTest.java index bae514e..2464f38 100644 --- a/src/test/java/com/auction/AuctionParsingTest.java +++ b/src/test/java/com/auction/AuctionParsingTest.java @@ -12,7 +12,6 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; -import java.util.regex.Pattern; import static org.junit.jupiter.api.Assertions.*; @@ -27,40 +26,40 @@ public class AuctionParsingTest { @BeforeAll public static void loadTestHtml() throws IOException { // Load the test HTML file - testHtml = Files.readString(Paths.get("src/test/resources/test.html")); + testHtml = Files.readString(Paths.get("src/test/resources/test_auctions.html")); System.out.println("Loaded test HTML (" + testHtml.length() + " characters)"); } @Test public void testParseAuctionsFromTestHtml() { // Parse the HTML with JSoup - var doc = Jsoup.parse(testHtml); + Document doc = Jsoup.parse(testHtml); // Find all auction links - var auctionLinks = doc.select("a[href^='/a/']"); + Elements auctionLinks = doc.select("a[href^='/a/']"); System.out.println("\n=== Auction Parsing Test ==="); System.out.println("Found " + auctionLinks.size() + " auction links"); List auctions = new ArrayList<>(); - var count = 0; + int count = 0; - for (var link : auctionLinks) { - var href = link.attr("href"); + for (Element link : auctionLinks) { + String href = link.attr("href"); // Extract auction ID from URL - var pattern = Pattern.compile("/a/.*?-A([17])-(\\d+)"); - var matcher = pattern.matcher(href); + java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("/a/.*?-A([17])-(\\d+)"); + java.util.regex.Matcher matcher = pattern.matcher(href); if (!matcher.find()) { continue; } - - var typeNum = matcher.group(1); - var auctionId = Integer.parseInt(matcher.group(2)); + + String typeNum = matcher.group(1); + int auctionId = Integer.parseInt(matcher.group(2)); // Extract auction info using IMPROVED text-based method - var auction = extractAuctionInfoFromText(link, href, auctionId, "A" + typeNum); + TroostwijkScraper.AuctionInfo auction = extractAuctionInfoFromText(link, href, auctionId, "A" + typeNum); auctions.add(auction); // Print the first 10 auctions for verification @@ -87,10 +86,10 @@ public class AuctionParsingTest { System.out.println("Total auctions parsed: " + auctions.size()); // Count by country - var nlCount = auctions.stream().filter(a -> "NL".equals(a.country)).count(); - var bgCount = auctions.stream().filter(a -> "BG".equals(a.country)).count(); - var deCount = auctions.stream().filter(a -> "DE".equals(a.country)).count(); - var beCount = auctions.stream().filter(a -> "BE".equals(a.country)).count(); + long nlCount = auctions.stream().filter(a -> "NL".equals(a.country)).count(); + long bgCount = auctions.stream().filter(a -> "BG".equals(a.country)).count(); + long deCount = auctions.stream().filter(a -> "DE".equals(a.country)).count(); + long beCount = auctions.stream().filter(a -> "BE".equals(a.country)).count(); System.out.println("Dutch (NL) auctions: " + nlCount); System.out.println("Bulgarian (BG) auctions: " + bgCount); @@ -102,171 +101,95 @@ public class AuctionParsingTest { assertTrue(auctions.size() > 0, "Should find at least one auction"); // Verify all auctions have basic info - for (var auction : auctions) { + for (TroostwijkScraper.AuctionInfo auction : auctions) { assertNotNull(auction.title, "Title should not be null for auction " + auction.auctionId); assertTrue(auction.title.length() > 0, "Title should not be empty for auction " + auction.auctionId); assertNotNull(auction.url, "URL should not be null for auction " + auction.auctionId); assertTrue(auction.auctionId > 0, "Auction ID should be positive"); + assertNotNull(auction.location, "Location should not be null for auction " + auction.auctionId); + assertNotNull(auction.country, "Country should not be null for auction " + auction.auctionId); + assertTrue(auction.lotCount > 0, "Lot count should be positive for auction " + auction.auctionId); } } - /** - * Helper method to extract auction info from a link element - * This is a copy of the logic from TroostwijkScraper for testing purposes - */ - private TroostwijkScraper.AuctionInfo extractAuctionInfo(Element link, String href, int auctionId, String type) { - var auction = new TroostwijkScraper.AuctionInfo(); - auction.auctionId = auctionId; - auction.type = type; - auction.url = "https://www.troostwijkauctions.com" + href; - - // Extract title from href (convert kebab-case to title) - var titlePattern = Pattern.compile("/a/(.+?)-A[17]-"); - var titleMatcher = titlePattern.matcher(href); - if (titleMatcher.find()) { - var slug = titleMatcher.group(1); - auction.title = slug.replace("-", " "); - // Capitalize first letter - if (!auction.title.isEmpty()) { - auction.title = auction.title.substring(0, 1).toUpperCase() + auction.title.substring(1); - } - } else { - auction.title = "Unknown Auction"; - } - - // Try to find title in link text (more accurate) - var linkText = link.text(); - if (!linkText.isEmpty() && !linkText.matches(".*\\d+.*")) { - // If link text doesn't contain numbers, it's likely the title - var parts = linkText.split(",|\\d+"); - if (parts.length > 0 && parts[0].trim().length() > 5) { - auction.title = parts[0].trim(); - } - } - - // Extract location using JSoup selectors - // Look for

tags that contain location info - var locationElements = link.select("p"); - for (var p : locationElements) { - var text = p.text(); - - // Pattern: "City, Country" or "City, Region, Country" - if (text.matches(".*[A-Z]{2}$")) { - // Ends with 2-letter country code - var countryCode = text.substring(text.length() - 2); - var cityPart = text.substring(0, text.length() - 2).trim(); - - // Remove trailing comma or whitespace - cityPart = cityPart.replaceAll("[,\\s]+$", ""); - - auction.country = countryCode; - auction.city = cityPart; - auction.location = cityPart + ", " + countryCode; - break; - } - } - - // Fallback: check HTML content directly - if (auction.country == null) { - var html = link.html(); - var locPattern = Pattern.compile( - "([A-Za-z][A-Za-z\\s,\\-']+?)\\s*(?:)?\\s*\\s*([A-Z]{2})(?![A-Za-z])"); - var locMatcher = locPattern.matcher(html); - - if (locMatcher.find()) { - var city = locMatcher.group(1).trim().replaceAll(",$", ""); - var country = locMatcher.group(2); - auction.city = city; - auction.country = country; - auction.location = city + ", " + country; - } - } - - // Extract lot count if available (kavels/lots) - var textElements = link.select("*"); - for (var elem : textElements) { - var text = elem.ownText(); - if (text.matches("\\d+\\s+(?:kavel|lot|item)s?.*")) { - var countPattern = Pattern.compile("(\\d+)"); - var countMatcher = countPattern.matcher(text); - if (countMatcher.find()) { - auction.lotCount = Integer.parseInt(countMatcher.group(1)); - break; - } - } - } - - return auction; - } - /** * IMPROVED: Extract auction info using .text() method * This parses the human-readable text instead of HTML markup + * + * Expected format: "[day] om [time] [lot_count] [title] [city], [CC]" + * Example: "woensdag om 18:00 1 Vrachtwagens voor bedrijfsvoertuigen Loßburg, DE" */ private TroostwijkScraper.AuctionInfo extractAuctionInfoFromText(Element link, String href, int auctionId, String type) { - var auction = new TroostwijkScraper.AuctionInfo(); + TroostwijkScraper.AuctionInfo auction = new TroostwijkScraper.AuctionInfo(); auction.auctionId = auctionId; auction.type = type; auction.url = "https://www.troostwijkauctions.com" + href; // Get ALL visible text from the link (this removes all HTML tags) - var allText = link.text(); + String allText = link.text().trim(); - // Extract title (usually the first meaningful text, before numbers/location) - var textParts = allText.split("\\s{2,}"); // Split on 2+ spaces - for (var part : textParts) { - part = part.trim(); - // Title is usually first long text without dates/numbers/commas - if (part.length() > 15 && !part.matches(".*\\d{1,2}[:/]\\d{2}.*") && !part.matches(".*,\\s*[A-Z]{2}$")) { - auction.title = part; - break; - } + // Pattern: "[day] om [time] [lot_count] [title] [city], [CC]" + // Example: "woensdag om 18:00 1 Vrachtwagens voor bedrijfsvoertuigen Loßburg, DE" + + // Step 1: Extract closing time (day + time) + java.util.regex.Pattern timePattern = java.util.regex.Pattern.compile( + "(\\w+)\\s+om\\s+(\\d{1,2}:\\d{2})" + ); + java.util.regex.Matcher timeMatcher = timePattern.matcher(allText); + + String remainingText = allText; + if (timeMatcher.find()) { + String day = timeMatcher.group(1); // e.g., "woensdag" + String time = timeMatcher.group(2); // e.g., "18:00" + + // Store closing time info (could be parsed to LocalDateTime with proper date) + System.out.println(" Closing time: " + day + " om " + time); + + // Remove the time part from text + remainingText = allText.substring(timeMatcher.end()).trim(); } - // Fallback: use URL slug for title - if (auction.title == null || auction.title.isEmpty()) { - var titlePattern = Pattern.compile("/a/(.+?)-A[17]-"); - var titleMatcher = titlePattern.matcher(href); - if (titleMatcher.find()) { - var slug = titleMatcher.group(1).replace("-", " "); - auction.title = slug.substring(0, 1).toUpperCase() + slug.substring(1); - } else { - auction.title = "Unknown Auction"; - } - } - - // Extract location (look for "City, CC" pattern where CC is 2-letter country code) - var locPattern = Pattern.compile( - "([A-Za-z][A-Za-z\\s\\-']+?),\\s*([A-Z]{2})(?![A-Za-z])" - ); - var locMatcher = locPattern.matcher(allText); + // Step 2: Extract location from the END (always ends with ", CC") + java.util.regex.Pattern locPattern = java.util.regex.Pattern.compile( + "([A-ZÀ-ÿa-z][A-ZÀ-ÿa-z\\s\\-'öäüßàèéêëïôùûç]+?),\\s*([A-Z]{2})\\s*$" + ); + java.util.regex.Matcher locMatcher = locPattern.matcher(remainingText); if (locMatcher.find()) { auction.city = locMatcher.group(1).trim(); auction.country = locMatcher.group(2); auction.location = auction.city + ", " + auction.country; + + // Remove location from end + remainingText = remainingText.substring(0, locMatcher.start()).trim(); } - // Extract lot count (look for "X kavels" or "X lots") - var lotPattern = Pattern.compile( - "(\\d+)\\s+(?:kavel|lot|item)s?" - ); - var lotMatcher = lotPattern.matcher(allText); + // Step 3: Extract lot count (first number after time) + java.util.regex.Pattern lotPattern = java.util.regex.Pattern.compile( + "^(\\d+)\\s+" + ); + java.util.regex.Matcher lotMatcher = lotPattern.matcher(remainingText); if (lotMatcher.find()) { auction.lotCount = Integer.parseInt(lotMatcher.group(1)); + + // Remove lot count from beginning + remainingText = remainingText.substring(lotMatcher.end()).trim(); } - // Extract closing time info (look for time patterns) - var timePattern = Pattern.compile( - "(\\d{1,2}:\\d{2})" - ); - var timeMatcher = timePattern.matcher(allText); - - if (timeMatcher.find()) { - var timeStr = timeMatcher.group(1); - // Note: Would need full date to convert to LocalDateTime - // For now, just log it + // Step 4: What remains is the title + if (!remainingText.isEmpty()) { + auction.title = remainingText; + } else { + // Fallback: use URL slug for title + java.util.regex.Pattern titlePattern = java.util.regex.Pattern.compile("/a/(.+?)-A[17]-"); + java.util.regex.Matcher titleMatcher = titlePattern.matcher(href); + if (titleMatcher.find()) { + String slug = titleMatcher.group(1).replace("-", " ").replace("%7C", "|"); + auction.title = slug.substring(0, 1).toUpperCase() + slug.substring(1); + } else { + auction.title = "Unknown Auction"; + } } return auction; @@ -277,26 +200,26 @@ public class AuctionParsingTest { System.out.println("\n=== Location Pattern Tests ==="); // Test different location formats - var testCases = new String[]{ - "

Amsterdam, NL

", - "

Sofia, BG

", - "

Berlin, DE

", - "Brussels,BE" - }; + String[] testCases = { + "

Amsterdam, NL

", + "

Sofia, BG

", + "

Berlin, DE

", + "Brussels,BE" + }; - for (var testHtml : testCases) { - var doc = Jsoup.parse(testHtml); - var elem = doc.select("p, span").first(); + for (String testHtml : testCases) { + Document doc = Jsoup.parse(testHtml); + Element elem = doc.select("p, span").first(); if (elem != null) { - var text = elem.text(); + String text = elem.text(); System.out.println("\nTest: " + testHtml); System.out.println("Text: " + text); // Test regex pattern if (text.matches(".*[A-Z]{2}$")) { - var countryCode = text.substring(text.length() - 2); - var cityPart = text.substring(0, text.length() - 2).trim().replaceAll("[,\\s]+$", ""); + String countryCode = text.substring(text.length() - 2); + String cityPart = text.substring(0, text.length() - 2).trim().replaceAll("[,\\s]+$", ""); System.out.println("→ Extracted: " + cityPart + ", " + countryCode); } else { System.out.println("→ No match"); @@ -304,4 +227,52 @@ public class AuctionParsingTest { } } } + + @Test + public void testFullTextPatternMatching() { + System.out.println("\n=== Full Text Pattern Tests ==="); + + // Test the complete auction text format + String[] testCases = { + "woensdag om 18:00 1 Vrachtwagens voor bedrijfsvoertuigen Loßburg, DE", + "maandag om 14:30 5 Industriële machines Amsterdam, NL", + "vrijdag om 10:00 12 Landbouwmachines Antwerpen, BE" + }; + + for (String testText : testCases) { + System.out.println("\nParsing: \"" + testText + "\""); + + // Simulated extraction + String remaining = testText; + + // Extract time + java.util.regex.Pattern timePattern = java.util.regex.Pattern.compile("(\\w+)\\s+om\\s+(\\d{1,2}:\\d{2})"); + java.util.regex.Matcher timeMatcher = timePattern.matcher(remaining); + if (timeMatcher.find()) { + System.out.println(" Time: " + timeMatcher.group(1) + " om " + timeMatcher.group(2)); + remaining = remaining.substring(timeMatcher.end()).trim(); + } + + // Extract location + java.util.regex.Pattern locPattern = java.util.regex.Pattern.compile( + "([A-ZÀ-ÿa-z][A-ZÀ-ÿa-z\\s\\-'öäüßàèéêëïôùûç]+?),\\s*([A-Z]{2})\\s*$" + ); + java.util.regex.Matcher locMatcher = locPattern.matcher(remaining); + if (locMatcher.find()) { + System.out.println(" Location: " + locMatcher.group(1) + ", " + locMatcher.group(2)); + remaining = remaining.substring(0, locMatcher.start()).trim(); + } + + // Extract lot count + java.util.regex.Pattern lotPattern = java.util.regex.Pattern.compile("^(\\d+)\\s+"); + java.util.regex.Matcher lotMatcher = lotPattern.matcher(remaining); + if (lotMatcher.find()) { + System.out.println(" Lot count: " + lotMatcher.group(1)); + remaining = remaining.substring(lotMatcher.end()).trim(); + } + + // What remains is title + System.out.println(" Title: " + remaining); + } + } } diff --git a/src/test/java/com/auction/Parser.java b/src/test/java/com/auction/Parser.java new file mode 100644 index 0000000..27be68d --- /dev/null +++ b/src/test/java/com/auction/Parser.java @@ -0,0 +1,64 @@ +package com.auction; + +import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter; +import com.vladsch.flexmark.util.data.DataHolder; +import net.bytebuddy.build.Plugin.Engine.Source.Element; +import org.jsoup.Jsoup; +import org.junit.jupiter.api.Test; + +import org.junit.jupiter.api.extension.Extensions; +public class Parser { + + public record AuctionItem( + String title, + String link, + int lotCount, + String location, + String closingTime + ) { } + + public static AuctionItem parseItem(String html, String baseUrl) { + var doc = Jsoup.parse(html, baseUrl); + + org.jsoup.nodes.Element li = doc.selectFirst("li.grid"); + if (li == null) return null; + + var linkEl = li.selectFirst("a[data-cy=item-link]"); + String link = linkEl != null ? linkEl.absUrl("href") : null; + + String title = text(li, "div.heading-6"); + + String closingTime = text(li, "[data-cy=end-time-text]"); + + String lotCountStr = text(li, "[data-cy=lot-count-text]").trim(); + int lotCount = lotCountStr.isEmpty() ? 0 : Integer.parseInt(lotCountStr); + + // Tweede span in de location grid + String location = li.select("[data-cy=location-text] span").size() >= 2 + ? li.select("[data-cy=location-text] span").get(1).text() + : null; + + return new AuctionItem(title, link, lotCount, location, closingTime); + } + + private static String text(org.jsoup.nodes.Element root, String css) { + var el = root.selectFirst(css); + return el != null ? el.text() : ""; + } + + @Test + void testbla() { + String html = "
  • 03:17:00
    \"\"
    \"\"
    \"\"
    \"\"
    115
    Sluiting van een metaalbewerkingsfabriek – CNC-bewerkingscentra, draadvonkmachine, gereedschapsmachines en meer
    Vahingen, DE
  • "; + var doc = Jsoup.parse(html, "https://www.troostwijkauctions.com"); + String markdown = FlexmarkHtmlConverter.builder().build().convert(html); + + System.out.println(doc.body()); + AuctionItem item = Parser.parseItem(html, "https://www.troostwijkauctions.com"); + + System.out.println(item.title()); + System.out.println(item.link()); + System.out.println(item.lotCount()); + System.out.println(item.location()); + System.out.println(item.closingTime()); + } +} \ No newline at end of file diff --git a/src/test/java/com/auction/TroostwijkScraperTest.java b/src/test/java/com/auction/TroostwijkScraperTest.java index df9362c..0b41454 100644 --- a/src/test/java/com/auction/TroostwijkScraperTest.java +++ b/src/test/java/com/auction/TroostwijkScraperTest.java @@ -67,28 +67,6 @@ public class TroostwijkScraperTest { } } - @Test - public void testDiscoverDutchAuctions() { - // Discover auctions from the live page - List auctions = scraper.discoverDutchAuctions(); - - // Verify that auctions were found - assertNotNull(auctions, "Auctions list should not be null"); - assertFalse(auctions.isEmpty(), "Should find at least one Dutch auction"); - - // Verify that all sale IDs are positive integers - for (Integer saleId : auctions) { - assertNotNull(saleId, "Sale ID should not be null"); - assertTrue(saleId > 0, "Sale ID should be positive: " + saleId); - } - - // Verify no duplicate sale IDs - long uniqueCount = auctions.stream().distinct().count(); - assertEquals(auctions.size(), uniqueCount, "All sale IDs should be unique"); - - System.out.println("✓ Found " + auctions.size() + " unique Dutch auctions"); - System.out.println("✓ Sale IDs: " + auctions); - } @Test public void testFetchAndPersistAuctionData() throws SQLException { diff --git a/src/test/resources/test.html b/src/test/resources/test.html index ce92f8f..66cc823 100644 --- a/src/test/resources/test.html +++ b/src/test/resources/test.html @@ -1,205 +1,5 @@ -Alle veilingen | Troostwijk Auctions

    Alle veilingen

    522 resultaten

    Woensdag 3 dec 25

    Download nu de Troostwijk Auctions app!

    App storeGoogle play
    \ No newline at end of file + + +

    Woensdag 3 dec 25

    + + \ No newline at end of file diff --git a/src/test/resources/test.md b/src/test/resources/test.md new file mode 100644 index 0000000..b0cee91 --- /dev/null +++ b/src/test/resources/test.md @@ -0,0 +1,456 @@ +## Woensdag 3 dec 25 + +* [ + + woensdag om 16:00 + + ![Industry & machinery](https://media.tbauctions.com/image-media/37f8e30d-7f4e-4af4-bb8a-029975b089cf/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/da3276a5-eb99-4a5d-ac3b-cc546b0a5f39/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/4d273787-cc80-4ac5-b89d-20525871426a/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/0b0b946d-26a6-486f-8c73-e9d0394e4e70/file?imageSize=1024x768 1024w) + + 145 + + Industrie & machines + + Meerdere locaties (45) + + + + + + ](/a/industrie-machines-A3-37358) +* [ + + woensdag om 16:00 + + ![D | Racing car transporters, crane polyp grabs and containers from inventory adjustment](https://media.tbauctions.com/image-media/b020da96-bb92-4e22-8a7b-2dd205dd5f7f/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/d3153444-7d59-45ac-8160-3cdaa7e1074e/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/be9f0e03-f585-47d0-abf0-3e802e21c8ad/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/658c88d7-0430-4454-b7fb-f48eeadeb401/file?imageSize=1024x768 1024w) + + 38 + + D | Raceautotransporters, kraan-polypengrepen en containers uit voorraadaanpassing + + Nieheim, DE + + + + + + ](/a/d-%7C-raceautotransporters-kraan-polypengrepen-en-containers-uit-voorraadaanpassing-A1-39772) +* [ + + woensdag om 16:00 + + ![Food Processing Equipment and Packaging Machinery](https://media.tbauctions.com/image-media/dd6b698d-79f5-40a4-ab74-2beca7a5341e/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/198b179c-0921-420f-9b14-ab430a957fb0/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/842f0a7d-cd05-4040-bb00-8691def7e9bc/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/b7855c86-f9e8-4346-9d0b-565d7cd126cc/file?imageSize=1024x768 1024w) + + 61 + + Voedselverwerkende apparatuur en verpakkingsmachines + + CHOMERAC, FR + + + + + + ](/a/voedselverwerkende-apparatuur-en-verpakkingsmachines-A1-39319) +* [ + + woensdag om 16:00 + + ![Agricultural & earthmoving machines](https://media.tbauctions.com/image-media/a11f32a9-5197-4486-8074-803b4da25227/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/dcf35ea2-0e1b-4d30-8cd7-1b534ecf7b0e/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/4857e24d-25b2-46f6-bde4-c0a5383508dd/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/7210f5e7-e124-40ab-b2db-c54e02af416a/file?imageSize=1024x768 1024w) + + 117 + + Landbouw- & grondverzetmachines + + Meerdere locaties (49) + + + + + + ](/a/landbouw-grondverzetmachines-A3-37375) +* [ + + woensdag om 17:00 + + ![Tools & equipment](https://media.tbauctions.com/image-media/24f212ce-e1f0-45a3-b095-c3944fd35340/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/1cc0cb65-68f6-4108-b51a-11cb1652f366/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/92d4629a-d259-43ad-806d-de99da15ea32/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/2653c057-5e28-46dc-aacc-08920f0500b4/file?imageSize=1024x768 1024w) + + 261 + + Gereedschappen & uitrusting + + Meerdere locaties (36), BE + + + + + + ](/a/gereedschappen-uitrusting-A3-37367) +* [ + + woensdag om 18:00 + + ![](https://media.tbauctions.com/image-media/13b74047-5372-493f-81dc-2d075c3bada1/file?imageSize=1024x768 1024w) + + 1 + + Vrachtwagens voor bedrijfsvoertuigen + + Loßburg, DE + + + + + + ](/a/vrachtwagens-voor-bedrijfsvoertuigen-A7-39531) +* [ + + woensdag om 19:00 + + ![White goods and accessories](https://media.tbauctions.com/image-media/1abf2d9b-7596-45e3-93b3-b503397eba0e/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/8cb4c2f1-f298-4e20-b221-ec828cca717c/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/d588cf94-3f82-4785-ace4-a8014a1859fd/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/9036f5bf-7fc7-43a3-ae8a-41c1376f60ec/file?imageSize=1024x768 1024w) + + 61 + + Witgoed en accessoires + + Etten-Leur, NL + + + + + + ](/a/witgoed-en-accessoires-A1-27241) +* [ + + Opent 28 nov 17:00 + + ![](https://media.tbauctions.com/image-media/4f41caa7-865a-4fe7-9cd5-305bd2e455f6/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/1cdf88f5-67d4-4932-96a4-449ad17ba51d/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/c47fee25-0326-40e1-b1db-3190a19ecb1a/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/a3e7c080-2dcf-45aa-99ab-2072697b4f54/file?imageSize=1024x768 1024w) + + 54 + + Collectie Rolex en Cartier horloges + + Dordrecht, NL + + + + + + ](/a/collectie-rolex-en-cartier-horloges-A1-39398) +* [ + + woensdag om 19:00 + + ![Kitchens and sanitary facilities](https://media.tbauctions.com/image-media/6379bf2c-aed2-4fbe-8fce-367c9d200141/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/16e97117-3cc2-4ced-bfe1-4f24e0b8d784/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/0002b9da-1ba5-429c-b377-eaddc3714e37/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/d3186016-0d7b-433a-b3e8-903fd85ec929/file?imageSize=1024x768 1024w) + + 254 + + SHOWROOMKEUKENS en INBOUWAPPARATUUR + + Tilburg, NL + + + + + + ](/a/showroomkeukens-en-inbouwapparatuur-A1-39480) +* [ + + woensdag om 19:00 + + ![](https://media.tbauctions.com/image-media/e6b76a75-9994-46b0-a2fa-c9a321f62980/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/ebef8850-5265-446a-ab32-5f2cd0d2bf88/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/f82e2b71-f908-472e-8019-9b2b15b0cb17/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/d92ba732-28b0-462f-bab5-ce82540d1c81/file?imageSize=1024x768 1024w) + + 499 + + Machines, retourgoederen en restpartijen + + Harlingen, NL + + + + + + ](/a/machines-retourgoederen-en-restpartijen-A1-39642) +* [ + + woensdag om 19:00 + + ![Lots of tools, office inventory, retail goods, decoration and olive trees](https://media.tbauctions.com/image-media/f949084a-50d5-4182-acfb-5d21ac54e471/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/96ef91fa-3927-49d4-af9e-ef709581ac51/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/2917a150-c99a-439f-8767-68ece5214800/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/328e4999-4a75-4f29-bd95-66b1de4e46e8/file?imageSize=1024x768 1024w) + + 120 + + Partijen gereedschap, kantoorinventaris, detailhandelgoederen, decoratie en olijfbomen + + Meerdere locaties (3), NL + + + + + + ](/a/partijen-gereedschap-kantoorinventaris-detailhandelgoederen-decoratie-en-olijfbomen-A1-27016) +* [ + + woensdag om 19:00 + + ![Bankruptcy vehicles](https://media.tbauctions.com/image-media/82880ed5-30f0-4055-99bd-a2cf07fad2ef/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/64312888-1b04-4f75-a0f1-386752232172/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/f4268e70-82fb-4ea1-ab92-c23dff650909/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/0c52cb25-fe99-4848-961b-a8a85c7a66d4/file?imageSize=1024x768 1024w) + + 16 + + Faillissementsvoertuigen + + Meerdere locaties (3), NL + + + + + + ](/a/faillissementsvoertuigen-A1-38368) +* [ + + woensdag om 19:00 + + ![](https://media.tbauctions.com/image-media/41066c8c-7806-43ee-beef-918c43e18cc7/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/a2543ed7-409e-449f-aa42-c68721432fdf/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/17dee71a-201f-40c5-9e8d-f71f807b27a9/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/0c8f8bb7-60fc-4c3f-af17-1080f489a8c2/file?imageSize=1024x768 1024w) + + 78 + + Personenauto’s, oldtimers, campers en brommobielen + + Buitenpost, NL + + + + + + ](/a/personenauto%E2%80%99s-oldtimers-campers-en-brommobielen-A1-39508) +* [ + + woensdag om 19:00 + + ![](https://media.tbauctions.com/image-media/278c35b9-3e5a-42eb-a09b-1bfdf48c60b2/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/2ced3007-a441-4752-84fc-eb3e2ceba7a2/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/36ed78ee-32f2-4824-b8eb-fd38f3d87e27/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/caa71786-c172-491f-bd78-787946e35480/file?imageSize=1024x768 1024w) + + 391 + + Bezorgveiling Faillissement Dvize B.V. – Hyundai Power Products gereedschappen + + Meerdere locaties (2) + + + + + + ](/a/bezorgveiling-faillissement-dvize-b-v-%E2%80%93-hyundai-power-products-gereedschappen-A1-39409) +* [ + + woensdag om 19:00 + + ![](https://media.tbauctions.com/image-media/37df514f-357a-43aa-9a5a-8fedefb7068f/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/37df514f-357a-43aa-9a5a-8fedefb7068f/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/37df514f-357a-43aa-9a5a-8fedefb7068f/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/4b8b6f24-4ca1-4141-a3e3-254caba98284/file?imageSize=1024x768 1024w) + + 208 + + Kunstplanten en bomen, composiet gevel- en vloerbekleding en akoestische materialen + + De Lier, NL + + + + + + ](/a/kunstplanten-en-bomen-composiet-gevel-en-vloerbekleding-en-akoestische-materialen-A1-28707) +* [ + + woensdag om 19:00 + + ![Metalworking machines, tools and stock in connection with company relocation](https://media.tbauctions.com/image-media/d5232c84-b313-48dc-973d-b82e762f000e/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/997e6803-7667-49ce-8a2c-b62939ee0aa7/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/40cec190-c669-4d80-b9fa-99039e6804ea/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/77371ea9-2869-4df6-97b4-145d76a34615/file?imageSize=1024x768 1024w) + + 181 + + Metaalbewerkingsmachines, gereedschap en voorraad in verband met bedrijfsverhuizing + + Cuijk, NL + + + + + + ](/a/metaalbewerkingsmachines-gereedschap-en-voorraad-in-verband-met-bedrijfsverhuizing-A1-39360) +* [ + + woensdag om 19:00 + + ![](https://media.tbauctions.com/image-media/2faa2b06-09c0-49ec-ba31-b8d24e15263a/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/485930ea-fbd4-4fc0-adeb-53f9e4f9b89e/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/fdb0a385-b27e-4aa2-b0c9-aca59d7c5f96/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/a268ff31-f1f1-421c-80e9-97cfd0fcfa7b/file?imageSize=1024x768 1024w) + + 238 + + Overstock en magazijnopruiming + + Heesch, NL + + + + + + ](/a/overstock-en-magazijnopruiming-A1-39538) +* [ + + woensdag om 19:00 + + ![Collector's Auction Scooters & Motorcycles](https://media.tbauctions.com/image-media/8464e9fc-b60a-4081-b898-d98328c8d1dd/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/a953420a-4d1d-4e80-9c53-be7bb85106c5/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/7f0198da-b213-46c0-8a0b-6a382ca1b029/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/7b11eee5-efe7-4fbd-8529-b1d50bd4db2e/file?imageSize=1024x768 1024w) + + 47 + + Verzamelveiling Scooters en Motoren + + Meerdere locaties (2), NL + + + + + + ](/a/verzamelveiling-scooters-en-motoren-A1-28428) +* [ + + woensdag om 19:00 + + ![Cars & transport](https://media.tbauctions.com/image-media/28be6ce7-6987-48ed-8758-622ab308ca2a/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/5be09c82-6f9a-41b8-b0dd-2d5a43327cb4/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/8fe5b954-b16c-4e72-a4b9-be6b345d5a82/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/183e08ae-4769-4a9d-a7db-2d07ab487781/file?imageSize=1024x768 1024w) + + 338 + + Auto's & transport + + Meerdere locaties (109) + + + + + + ](/a/auto%27s-transport-A3-37349) +* [ + + woensdag om 19:30 + + ![](https://media.tbauctions.com/image-media/f1401ff5-4e5d-41e5-b4b2-6771fd7aad83/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/d788438c-5a47-4eeb-aced-4777c5bb4701/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/f888c72a-8756-4b83-994d-a4bb6a08eb05/file?imageSize=1024x768 1024w) + + ![](https://media.tbauctions.com/image-media/9566453d-2c19-431c-b5c6-521cfdc01594/file?imageSize=1024x768 1024w) + + 74 + + Gouden juwelen en diamanten + + Meerdere locaties (28) + + + + + + ](/a/gouden-juwelen-en-diamanten-A1-29562) \ No newline at end of file diff --git a/troostwijk.db b/troostwijk.db index 729611b8976b7ffe671eebc822e2db8ae3ce89a7..286200e7fe2c3fdf116b06443c7e987fc7e339a0 100644 GIT binary patch delta 499 zcmZ8c%SyvQ6ioyt#TZ)`(nSc&vc)KZ^aJ`Br4UmosdVKKlR*Z?M4D-7R%vl1xKR9o z;tv$<5BVoGX);B6;c}nn+J}xQ7md@!<+mk79+8mk&9lffY;}2U#S{~{Bp|O2S&tP zL{PlzG+P$hqG7F=r=n6N4!TBtc-!yZeEw=mVsgqEfesov(C(!^uq=~>o|`?sX`3G9 zagpUR@?tzHhmg-;e?Eck6kVx T^_8vtw7y_PK3K_>b-4(CVWywH delta 61 zcmZo@U}{*vI6+#FnSp_U6^LPgd7_T7C^LgzStl=0h=tdWfxm|@jCTRA-)2F9TRfZn IcsDr#04Bi;PXGV_