From 64b9123a519b3669a6d08ab3836765f2c7cebd57 Mon Sep 17 00:00:00 2001 From: Stefan Bratanov Date: Sat, 4 Nov 2023 09:38:27 +0000 Subject: [PATCH] Fix lidl parsing of products (and use coroutines) --- build.gradle.kts | 1 + .../extractors/LidlProductExtractor.kt | 55 + .../extractors/LidlProductsExtractor.kt | 83 +- .../extractors/LidlProductExtractorTest.kt | 42 + .../extractors/LidlProductsExtractorTest.kt | 47 +- .../resources/extractors/lidl/expected.json | 696 +- .../extractors/lidl/input-single.html | 1008 ++ src/test/resources/extractors/lidl/input.html | 14406 +++++++++------- src/test/resources/logback-test.xml | 3 + 9 files changed, 10094 insertions(+), 6247 deletions(-) create mode 100644 src/main/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductExtractor.kt create mode 100644 src/test/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductExtractorTest.kt create mode 100644 src/test/resources/extractors/lidl/input-single.html diff --git a/build.gradle.kts b/build.gradle.kts index c66b7ab..0776c2c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.apache.commons:commons-lang3") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") diff --git a/src/main/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductExtractor.kt b/src/main/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductExtractor.kt new file mode 100644 index 0000000..c63077b --- /dev/null +++ b/src/main/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductExtractor.kt @@ -0,0 +1,55 @@ +package com.stefanbratanov.sofiasupermarketsapi.extractors + +import com.stefanbratanov.sofiasupermarketsapi.common.Log +import com.stefanbratanov.sofiasupermarketsapi.common.Log.Companion.log +import com.stefanbratanov.sofiasupermarketsapi.common.getHtmlDocument +import com.stefanbratanov.sofiasupermarketsapi.common.normalizePrice +import com.stefanbratanov.sofiasupermarketsapi.model.Product +import java.net.URL +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import org.apache.commons.lang3.StringUtils +import org.springframework.stereotype.Component + +@Log +@Component +class LidlProductExtractor { + + fun extract(url: URL): Product { + log.debug("Processing Lidl product URL: {}", url) + + val document = getHtmlDocument(url) + + val name = document.selectFirst("h1.keyfacts__title")?.text() + val quantity = document.selectFirst("div.price-footer")?.text() + val price = document.selectFirst("div.m-price__price")?.text() + val oldPrice = document.selectFirst("span.m-price__rrp")?.text() + + val dateRange = + document.selectFirst("span[data-v-35dadb86]")?.text()?.trim()?.let { dateSpan -> + "\\d+.\\d+.".toRegex().findAll(dateSpan).map { date -> + val match = date.groupValues[0] + try { + LocalDate.parse( + match.plus(LocalDate.now().year), + DateTimeFormatter.ofPattern("dd.MM.yyyy"), + ) + } catch (ex: Exception) { + log.error("Error while parsing $date", ex) + null + } + } + } + + return Product( + name = StringUtils.normalizeSpace(name), + quantity = StringUtils.normalizeSpace(quantity), + price = normalizePrice(price), + oldPrice = normalizePrice(oldPrice), + category = null, + picUrl = null, + validFrom = dateRange?.elementAtOrNull(0), + validUntil = dateRange?.elementAtOrNull(1) + ) + } +} diff --git a/src/main/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductsExtractor.kt b/src/main/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductsExtractor.kt index b9b38f7..663ed01 100644 --- a/src/main/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductsExtractor.kt +++ b/src/main/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductsExtractor.kt @@ -2,86 +2,45 @@ package com.stefanbratanov.sofiasupermarketsapi.extractors import com.stefanbratanov.sofiasupermarketsapi.common.Log import com.stefanbratanov.sofiasupermarketsapi.common.Log.Companion.log -import com.stefanbratanov.sofiasupermarketsapi.common.UrlValidator import com.stefanbratanov.sofiasupermarketsapi.common.getHtmlDocument -import com.stefanbratanov.sofiasupermarketsapi.common.normalizePrice import com.stefanbratanov.sofiasupermarketsapi.interfaces.UrlProductsExtractor import com.stefanbratanov.sofiasupermarketsapi.model.Product import java.net.URL -import java.time.LocalDate -import java.time.format.DateTimeFormatter import java.util.* -import org.apache.commons.lang3.StringUtils +import kotlinx.coroutines.* +import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component @Log @Component("Lidl") class LidlProductsExtractor( - private val urlValidator: UrlValidator = UrlValidator(), + @Value("\${lidl.base.url}") private val baseUrl: URL, + val lidlProductExtractor: LidlProductExtractor ) : UrlProductsExtractor { + @OptIn(DelicateCoroutinesApi::class) override fun extract(url: URL): List { - log.info("Processing Lidl URL: {}", url.toString()) + log.info("Processing Lidl URL: {}", url) val document = getHtmlDocument(url) - val category = document.selectFirst("meta[property=og:title]")?.attr("content") - - return document - .select("article[data-price]") - .filter { element -> !element.select(".lidl-m-pricebox__price").isEmpty() } - .map { - val dateRange = - it.selectFirst(".lidl-m-ribbon-item__text")?.text()?.trim()?.let { dateSpan -> - "\\d+.\\d+.".toRegex().findAll(dateSpan).map { date -> - val match = date.groupValues[0] - try { - LocalDate.parse( - match.plus(LocalDate.now().year), - DateTimeFormatter.ofPattern("dd.MM.yyyy"), - ) - } catch (ex: Exception) { - log.error("Error while parsing $date", ex) - null - } + val category = document.title() + + val deferredProducts = + document.select("div[data-selector=PRODUCT]").mapNotNull { + val picUrl = it.attr("image").takeIf { url -> url.isNotEmpty() } + it + // retrieve product URL + .attr("canonicalurl") + .takeIf { canonicalUrl -> canonicalUrl.isNotEmpty() } + ?.let { canonicalUrl -> baseUrl.toURI().resolve(canonicalUrl).toURL() } + ?.let { productUrl -> + GlobalScope.async { + lidlProductExtractor.extract(productUrl).copy(category = category, picUrl = picUrl) } } - - val name = it.attr("data-name") - val oldPrice = - it - .select(".lidl-m-pricebox__discount-price") - .textNodes() - .takeIf { tn -> tn.isNotEmpty() } - ?.first() - ?.text() - val newPrice = it.selectFirst(".lidl-m-pricebox__price")?.text() - val quantity = it.selectFirst(".lidl-m-pricebox__basic-quantity")?.text() - - var picUrl = - it - .select("picture") - .select("source[data-srcset]") - .eachAttr("data-srcset") - .firstOrNull { srcSet -> srcSet.contains("/sm/") } - ?.split(",") - ?.map { picUrl -> picUrl.replace(Regex("\\dx\\s*\$"), "").trim() } - ?.firstOrNull { picUrl -> urlValidator.isValid(picUrl) } - - if (Objects.isNull(picUrl)) { - picUrl = it.select("picture").select("source").attr("srcset") - } - - Product( - name = StringUtils.normalizeSpace(name), - price = normalizePrice(newPrice), - oldPrice = normalizePrice(oldPrice), - quantity = StringUtils.normalizeSpace(quantity), - picUrl = picUrl, - category = category, - validFrom = dateRange?.elementAtOrNull(0), - validUntil = dateRange?.elementAtOrNull(1), - ) } + + return runBlocking { deferredProducts.awaitAll() } } } diff --git a/src/test/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductExtractorTest.kt b/src/test/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductExtractorTest.kt new file mode 100644 index 0000000..32a4a8c --- /dev/null +++ b/src/test/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductExtractorTest.kt @@ -0,0 +1,42 @@ +package com.stefanbratanov.sofiasupermarketsapi.extractors + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isEqualToIgnoringGivenProperties +import com.stefanbratanov.sofiasupermarketsapi.getUri +import com.stefanbratanov.sofiasupermarketsapi.model.Product +import java.time.LocalDate +import java.time.Month +import org.junit.jupiter.api.Test + +internal class LidlProductExtractorTest { + + private val underTest = LidlProductExtractor() + + @Test + fun `test extracting product`() { + val testHtmlUrl = getUri("/extractors/lidl/input-single.html").toURL() + + val product = underTest.extract(testHtmlUrl) + + val expectedProduct = + Product( + name = "Багета Рустик", + quantity = "250 g/бр.", + price = 0.99, + oldPrice = 1.99, + category = null, + picUrl = null + ) + + assertThat(product) + .isEqualToIgnoringGivenProperties(expectedProduct, Product::validFrom, Product::validUntil) + + assertThat(product.validFrom?.dayOfMonth).isEqualTo(30) + assertThat(product.validFrom?.month).isEqualTo(Month.OCTOBER) + assertThat(product.validFrom?.year).isEqualTo(LocalDate.now().year) + assertThat(product.validUntil?.dayOfMonth).isEqualTo(5) + assertThat(product.validUntil?.month).isEqualTo(Month.NOVEMBER) + assertThat(product.validUntil?.year).isEqualTo(LocalDate.now().year) + } +} diff --git a/src/test/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductsExtractorTest.kt b/src/test/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductsExtractorTest.kt index d946bb9..40ff482 100644 --- a/src/test/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductsExtractorTest.kt +++ b/src/test/kotlin/com/stefanbratanov/sofiasupermarketsapi/extractors/LidlProductsExtractorTest.kt @@ -3,25 +3,40 @@ package com.stefanbratanov.sofiasupermarketsapi.extractors import assertk.assertThat import assertk.assertions.isNotEmpty import com.stefanbratanov.sofiasupermarketsapi.getUri +import com.stefanbratanov.sofiasupermarketsapi.model.Product import com.stefanbratanov.sofiasupermarketsapi.readResource import com.stefanbratanov.sofiasupermarketsapi.testObjectMapper +import io.mockk.every +import io.mockk.mockk import java.net.URL import java.time.LocalDate import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test -import org.skyscreamer.jsonassert.Customization import org.skyscreamer.jsonassert.JSONAssert import org.skyscreamer.jsonassert.JSONCompareMode -import org.skyscreamer.jsonassert.comparator.CustomComparator internal class LidlProductsExtractorTest { private val objectMapper = testObjectMapper() - private val underTest = LidlProductsExtractor() + private val lidlProductExtractor: LidlProductExtractor = mockk() + + private val underTest = LidlProductsExtractor(URL("https://www.lidl.bg"), lidlProductExtractor) @Test fun `test extracting products`() { + every { lidlProductExtractor.extract(any()) } returns + Product( + name = "foo", + "1 кг", + 8.99, + 10.99, + category = null, + picUrl = null, + validFrom = LocalDate.of(1993, 7, 28), + validUntil = LocalDate.of(2023, 11, 4) + ) + val testHtmlUrl = getUri("/extractors/lidl/input.html").toURL() val products = underTest.extract(testHtmlUrl) @@ -29,31 +44,23 @@ internal class LidlProductsExtractorTest { val actualJson = objectMapper.writeValueAsString(products) val expectedJson = readResource("/extractors/lidl/expected.json") - val customization: (actualField: Any, expectedField: Any) -> Boolean = - { actualField, expectedField -> - LocalDate.parse(expectedField.toString()) - .withYear(LocalDate.now().year) - .equals(LocalDate.parse(actualField.toString())) - } - // use current year for comparison - JSONAssert.assertEquals( - expectedJson, - actualJson, - CustomComparator( - JSONCompareMode.STRICT, - Customization("[*].validFrom", customization), - Customization("[*].validUntil", customization), - ), - ) + JSONAssert.assertEquals(expectedJson, actualJson, JSONCompareMode.STRICT) } @Test @Disabled("used for manual testing") fun `test fetching from real url`() { - val lidlUrl = URL("https://www.lidl.bg/bg/c/niska-cena-visoko-kachestvo/c1847/w1") + // use real product extractor + val underTest = LidlProductsExtractor(URL("https://www.lidl.bg"), LidlProductExtractor()) + val lidlUrl = + URL( + "https://www.lidl.bg/c/niska-tsena-visoko-kachestvo/a10031916?channel=store&tabCode=Current_Sales_Week" + ) val products = underTest.extract(lidlUrl) + products.forEach { println(it) } + assertThat(products).isNotEmpty() } } diff --git a/src/test/resources/extractors/lidl/expected.json b/src/test/resources/extractors/lidl/expected.json index 1da1e52..16df5e7 100644 --- a/src/test/resources/extractors/lidl/expected.json +++ b/src/test/resources/extractors/lidl/expected.json @@ -1,62 +1,642 @@ [ { - "name": "Шварцвалдска шунка", - "quantity": "2 х 100 g/опаковка", - "price": 3.79, - "oldPrice": 7.58, - "category": "Двойно повече на същата цена", - "picUrl": "https://bg.cat-ret.assets.lidl/catalog5media/bg/article/10707/third/sm/10707_55.jpg", - "validFrom": "2023-01-23", - "validUntil": "2023-01-29" - }, - { - "name": "Пастет с риба тон", - "quantity": "2 х 95 g/опаковка", - "price": 2.49, - "oldPrice": 4.98, - "category": "Двойно повече на същата цена", - "picUrl": "https://bg.cat-ret.assets.lidl/catalog5media/bg/article/7303088/third/sm/7303088_88.jpg", - "validFrom": "2023-01-23", - "validUntil": "2023-01-29" - }, - { - "name": "Бърбън кола", - "quantity": "2 х 0,33 l/опаковка", - "price": 2.69, - "oldPrice": 5.38, - "category": "Двойно повече на същата цена", - "picUrl": "https://bg.cat-ret.assets.lidl/catalog5media/bg/article/161674/third/sm/161674_88.jpg", - "validFrom": "2023-01-23", - "validUntil": "2023-01-29" - }, - { - "name": "Шунка Pikok Pure", - "quantity": "2 х 180 g/опаковка", - "price": 5.99, - "oldPrice": 11.98, - "category": "Двойно повече на същата цена", - "picUrl": "https://bg.cat-ret.assets.lidl/catalog5media/bg/article/7202363/third/sm/7202363_0.jpg", - "validFrom": "2023-01-23", - "validUntil": "2023-01-29" - }, - { - "name": "Тесто за пица", - "quantity": "2 х 400 g/опаковка", - "price": 1.99, - "oldPrice": 3.98, - "category": "Двойно повече на същата цена", - "picUrl": "https://bg.cat-ret.assets.lidl/catalog5media/bg/article/999230403/third/sm/999230403_0.jpg", - "validFrom": "2023-01-23", - "validUntil": "2023-01-29" - }, - { - "name": "Grivas Чипс от царевица", - "quantity": "2 х 100 g/опаковка", - "price": 2.19, - "oldPrice": 4.38, - "category": "Двойно повече на същата цена", - "picUrl": "https://bg.cat-ret.assets.lidl/catalog5media/bg/article/999230401/third/sm/999230401_0.jpg", - "validFrom": "2023-01-23", - "validUntil": "2023-01-29" + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/iV3VwFsL3GJ61sFsNnVzLuJObL_JWpmI6LwjbR6CvUQ/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS84NkVBMENFQzQ4RDlENjBGRjM3NDY5QkE/3MEY4RDhGMDM0RDcxRTY4ODc5NEU5MDE5QTU5RTE0REUxRDBBRTE0LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/K3yE8-l9HOFcZSljVTYXGW-447SHosNL-t3e2PdCE0I/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9DRTVFQzJFRDEyNzAzNjhDNjBBOUY5MkI/0Nzk4MURFM0FGOTg1OTMxMkUzOEE1OUVCQzZBNDhBNzU3NjBFQzg1LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/1HDR7vhQxWUT92VU6WjUQqtZDtnHlGSmQcAJzEDEOJ4/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8yQTk4RDcxQUVBMTlGRjlCREFGMEU0Mzk/3RDVEMjk0NTQ1NTI4M0IxQjM0NTAyM0U2REZCRjEzNzIxMUUwMjRBLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/MbdkKi3RRfuoW6sfcZ9ziaa88-RIcBJExd2hVOSJX4Q/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS80QkNFMEMxRTFBQTRDMUZERTI4NkY1Qjd/ENTFEODkxNzg5QUE5MUUyOUEzOEQ4REI5RkZBQkU2OEZEMDE0NkQxLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/a8gBeMQU5gvEXkKNbUU5EWWGDx1zqfwSzAWh-NTdrDI/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS82NDEwMkVGNjU2OERGNTVDNDFDREZDMDV/DRDkwMzJDNUI2Q0VBRjYxOTBGRjdDNEE2MkY0OEQ1MTcwNjlGQUQ4LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/95OdJgNjnpSWTBsOw1k7M_uWSIbSRTXnZ3WXxUqdhjQ/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8xRTVBQjMwNTc3NEE4QzUyNEY2MEU1NDM/wOTA3MUFCNDVGNkU4N0Q4NTAwMTQxNjc2MkU0NkU1NDEzNzk0NzI4LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/ySBkLQMTRahKshS0jHulf5FrHju7C9zi9KVVh9cnRpU/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9COEM3M0M4NDNENkU5RDY4REFDQzdFNjE/4QTcwMTlGMzg2OEJCREVDNjM4NUYyMDRFODZGNDM3MjQ2OEVDQjE5LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/qUc15vH8Zcdb4nbDMyLaF6LSe1P-IVhno7wcjNHaRUw/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS85NjcyM0U5RTY2MTlCN0ZDMjlEREVCMTI/xQzZENDE5ODc5MDUyRkJGOTg1NUYwM0E1RTkzQjAwN0YxQTY0QzA0LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/5t-p2E6Znap5vpMpaOz_Ws2T1zuKz22p_6Mj7l6n1KI/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS81MUI3MzVBQURCNUUxNkFENzk0NDQ0NjE/yQzQzMDg0QzIyMUVFNkZDM0IxOUU1MEVGOTA1MUUwRjNBRTFEOEMwLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/JLz3r4QolJhATpsFtwlupggoW1qp_RIWqv3hSHMG-5E/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9DMEJDMkVGMURCOTU4OUI3Njc2MUQ5NTc/2NzExRUZFMzRENUIwQjNEQjgyM0Y0MjRFMTVERkE5QzA3QTUyQkRFLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/pwdKF6xhPMqpGcB7_ml3VBN3zcf5AA2eNFUKdCDJHRo/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8wMzYwNzZBMkM1NEE4RUYxRkZEMDUyOTE/zRkMwNUEzMzM2NDY2MjEwNkNDOTBBRjQzMUU0MDRDOERCOUI4Qzg3LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/nGRWRBYqYfwuOY1mQcuTMvhBhE0PJX_lHbWmQF0GgKg/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9GMEM4MDlEMERENzZFRjYwMTUzREQwMUR/CNzY5NTczQTE5NkVEQUExOUU2QThEOEY1MjVDN0VCMkU1QzEwRUZGLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/Cd39VRGodu5CJgPjJwhkLmBAx2lDmcZLDmLmP5c47Zk/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8yNTdDQjA1Q0IwNzFEMDE4RUQ3QkM0QTE/4QTI0MzE5MkYxM0NCMUNGNUI0ODBFODBBMjM1Q0Y2RUYzQjAyQzU0LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/5xjkR42xpOTxTSyk4JRbueZcKlDt0Oau2WY1rh_AsM8/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS83ODA3REMzNzlCQTQzODNBRjcwRTQ3M0M/0Nzg5NDAzODMzQUQzRDNERkY1NEE2QTlGNTQ1RkMxNUM1MTEyNjNCLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/P7_8NBrB6NChrP5WSw0wWzP2_ux2NM6EdRQZuvJr3is/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS85QURGODQyMjc4N0QxRUFFMEQ4ODY3NTE/2MjA5Q0ZEMDBEQjVCQzc1ODFBNTU2QkNCMDFDNDFDNkYzQ0VBOTAxLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/C5IOUeGWJ6tZ0TJ8psOKZx6pjnUw1L5tg1wNEUUx9ms/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8wOUVCQ0M3OTQzQTgzRkZBNEQxOUNEQUI/zMDUwRDgzMjRBM0VFREU1RjZENzRGMEUwRTlGNEFGNjczODNBRTQzLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/055kbJ4oGZE38ofKw8RhO-dz-eic7IdeOGtVz4D0tcA/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS83NjA3QTAwMDM4RUU3N0ZCNUIzQkM4QjE/xODBDMzFDM0Y5RjE0MkM1NTcxMTIzRThBQzlGQzY4RDUzNDNDQTUzLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/DJB33Bt1P8OF4OljVpx5934SohuICE11C7WZ6uomR1o/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9DOTc3MjJDNEEwQjYzRTdDNjdGNEVFQTA/2NDI4RDg5NzcyMTRFM0E2Q0RFRUJEMDhGN0Y4NEM3Qzg2MjJFRUFELmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/IPe5JnErmhgpjCwwYu70MZ8cHqjOqdbiXXvMCy7Pqas/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS85QkVCNzhGMDdERUZCRDJBRTNDRTgxQTV/COTRDMkRCNENFNjZBNkVCNzZBNDUyNjlDQUJEREEyRTkxNUNGMTQwLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/mWxFO0ZRzzVg3545m6l5tL619gThMcN7YmrqAYWcLiQ/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9BQzJBQkE5OURGNkNFMkJEMERCMDJDQTc/yRUQ0OTE4NEY5MjNBNTZFOEExNUM0Q0YwMkJGOUZCQkNDOUQ4OTgwLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/3Em_FUwBkhT6Q2DoA3IZIRNluZT49mrQBPEvyFUoc3w/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9FQzA5MzM0MjM4MzkwMUVDN0JENEUxNzE/wRkFERUNFMUY3QTk4QjNGOTZCRUYzQzE0N0ZCNDM5RjQxN0RFNEVBLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/RyePdWhCJenU4dg3IYCxKsWI9nPIhehmAr5P2_BWLvQ/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS84NkNFNEUyOEVERTJBMzZCMTY0NDBBMjJ/CQUY3MDVEQzg4M0MzMEFGMkJCNkYwMzMxODUwQTdDMDE0RTkxNjU2LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/Wt_mnaLS7cmi4ROVJ4ZCegAn1lacienGgRTWJkG_piY/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS81NEUwNUY5ODQxQkQzNTBDRkRBM0M2QTY/1MDQ0Njc1MkU2RjE5Njc2MDYxRDY2RkI4NjVEMzA0QUYyNjkxMDVCLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/B7ovlWecEp7PZ6eK0kFhMQx5RxJfvNG-j_lVXrcIlFM/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8yQ0I5NDEyMzQ4Q0Y2NzEwRjY4RUVGNkE/wRDk4REMxRTEwRTYzQkUyNUU4OTdFNTZCMzI3QzlBMDc3MzVDOTgxLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/DFaz2K5JKM9ixqnT8BDGPDHnLA_TTZjYTSBodDxTw1Y/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8xMEFEMTU0OTI3NjY1NTYxNjcwMjYxMjU/zRjZBODkzNTJCNDFDQTZBNkUyRUNFOTVBQUY5NUUzNzQwNTlGQzY3LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/_KWwOtelW57C1XTt2upOEbJiqar6YNUcDsspoawKth4/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9FM0RCNjU0RjdBMEJBQzlBQzU0OEQ5MDF/DOUVBMzZGN0Y5ODcxQzg4RDQ0MERFQkQwRTk4MjAwQUMwNEY3MzdBLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/t8YDWtKgOtBXDNS40CgSDt1yyU-oETfLDOiUcgeRDI0/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9CNEU2OEE1NjhGQjNCRUU2NjY0NzIyNUY/xNTI5NjE0MENGMTg3M0U1NkUxQ0JGMDU2RjI1NEQyNzQ0NzlBNDc3LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/X3wX11QmUswATAgm9HEsylbVmjd7n74A9sc8PdsUwx0/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS80QTYwMzFDMTE3RTg5MzdFODg2MjU4MTQ/2MEZDQkM3MTQ0N0Y3REUxMDJCRUQyMDU4NTM4QzE1QTcxMTYwNjdCLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/zngT6L47ixJpL_8kQiR8NmOjOIrLOkEaGAb7XE7mJRI/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9FOTMzNEExQkY4QzE2MkI2QTMwRDQ1QTA/zNDVEOUFFRTRFMEY4Rjg4MzFCNkM0NzI1RTZDRkE5ODIyNDEzMDA3LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/yvY-nTzzXKfPhpGi3Wuqarz7ffIZGmzgpQEZ3UZx-x8/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS80NkU4MjQzQUFDNEY4MTg5N0RDOTBERTZ/GOEIxQjI0RTU3Q0Y4QzY5RjgxM0JGQ0MzRUJFRkNBNDNGMzIyNjlFLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/zg58iQBdZcHUE6U6vgO_jsC41j8GkMVhOMF6mbCnkws/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS83QUIwM0VCNDUzRkIyQjYyQUJEODVGOTF/EOTc1RjBBRjZDQzczMEFBNThBOEZBM0Y0RjFCMjQ5RkI3NEIxMDA0LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/Nv2q680IdDz-9fhsUUcw6ngNljKfMtiIG9lAIXgNXho/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9GMTRBNkEwNjJFMzg4OTkxQTkxRDM4M0V/DMzc3ODI2NEY1NTA1RTQ2RjM5NzVBMjBDODUzQkEzNTk3NjJFNTVCLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/JbTNSTBFgIa3JehAOXImIs-iVKINpFHD-I55kdDEiMQ/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8zNjU2QTMyRjMwOEE2Mjg2MTRGRUU5Njl/DQjEzNjE4MDdGMEQ1MDc1MEEwMkQ0QUJEOUZEN0JDRTMzQTcwNDg4LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/DoZeW9v1RWntF0N4z74jzqPTp3ZdhWATA7pm9pZePQY/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS84OTZBNEI4QjIyRkNCMjNFRjhGMERDQTl/BMjkwNjYxQjMzRkYzRUI1Mjc2OEYzNDY5OUYxMkNCMDlDODZEN0Q4LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/OnfEGcwcjG092jPn6cvOq5qeBybkDeziDVq1RyS2I9I/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9CQ0YwMEU4N0NFRkU2ODdGQkRGNUM0QTR/FRUU3Q0JENjNEQTkwQjJBMzc1RDlENjkwQjhFNDNBQkNEMjdBNUM4LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/B0hjrEP-4t7gU_Qo26Sq4ALyH-FZssDysUsp0e9RbfQ/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS81NjA2NEVGMkY1M0U3OUM2NDVCNjgxQkR/DNEFCNjgzNDAzNTE4RUExQjNFOTkyQTQ2OEQ5RDZCQUM4QzFERUQyLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/eB9zS8WcyodA90ZyNu3XKAm78Vln90o2MWvUWCS_kN4/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS83ODZGRDI2NkYzNUEyRDg0NkRCQ0FCOTZ/ENjIyMTI2NkY5M0MwMDdFNzVDMUYzREVCQjk1RUFFQjMwMDc1MDBCLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/cKUtQGn48sNVoaA8hozZk3jLcgJurfexC6UxRL-vXME/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8yRjg2QUI5RDQ3MEU1QTZCNDlCOTRENDF/BMUY4Q0IzQTZGRENDMkYxMUYxRjNGOEUxNDkwRDY1RUE3RDkzNDM2LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/6-LQkh8c4oiF_9ZT575TNJk6KoiSUqmvx05AOnfeJQU/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS80RDQ4M0RCNkI3NzRFRjg4REM5QTA0Q0Q/3N0M1NjY2RTY5MEY3MkVCQjUyNTRFODUyNTdGQjEyQjM0QTZCNjJDLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/IIiV4Mo2TwBqW4--UGt5-gv8Chl6eoojU1inwRroDfM/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8xRDYwQ0M3QTg0NEIxNUYxOTlCOURBNEU/0QUQ1QTc4RkRFMDIyQTlFM0RCNEQxM0EyODM1QzBBRUJCRkRDNEIwLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/PW_Lkr4xhrrjey9Rk_a1bb-E9xzal2KDoUo55BDYNi0/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS84QTE3NEVBQ0M1NTI0N0FBMEMzQTA0QUV/BMDBDRTlCRDNDNEFFRDVBNjQwNTI3OTM2MUVCNTA5OEQ2QTUwNDQ3LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/pJn2-YE-JiHdu1wyozcI22hWdtg4oS2m4bHiYyrk5yw/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9ERTJBRjQ3MjVDMDg5M0Y4NDlGQTE0Qzk/2NUM0OTU4NzEzQkUwRjRCNkZDMDhCRkQyRkUzOEJFQjE3QTcxNkEzLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/sFGFn_fsUfYmBIyvWYP8AAxf3QacZHHhgNGfonVMf2M/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9EQ0QwQjVFMkJBRDQ4RDMwMzExMDBGRUM/wMDY1RUEzQTE0OERDRjQ2Q0E3MDM2NTZFOEFGQzBCNzZFRDRDOTlCLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/_ArvfWg8wxms7_fDFwrC1zm-hIKA9a_nJOWEW0OrfEg/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS85RDhEQjE2NkMyNzUxNkI5QkIyNEE3NUN/FNEU3MzgwNEUwQjE4RTM5NThFRjE2ODBFRjc4MTU2MTREODZCMzUwLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/EpCiDIj2YGc3Ksjppg7MvK-1Z5QNRmRUfvffXTcEnII/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8xNDk2NTVDOEM5NUEwNEM1NEZFRjUyQ0Q/wRUM1M0E4OTMxODgzOTI0QkU2RDQyMkMxRTZGRUY3QzU0ODc5NDk3LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/1GI1RGM_PDOmVf2rCHegIRrUm-Q2vjvlLwKU_AtfWEU/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS85QjBFMEYyMkI2NDM2MENFNTAxN0JBQUU/yRTQ0MzVCQzJBQTk0MDg5NUIwNTNDQjEwMDQ3NzA1RDE5Njc2NTk5LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/Ont55RpIuGQdBVGo5lsAxN1E-O6Mv4M1wIntzys7HgE/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS85MUZCRTM1RDhCN0I0RDU1Q0VGOUEwNjI/2QjA5MDMzNTYxMjAyMzBENzBDMkY3MDg3MTFCOUY3M0JCM0FDQjJCLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/Cgo96uInyTxeVfPgaPYMSumduIbqZ3sCXYpA8S3KYTU/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS82NUM2REFGNDFDMTlBOUJEQjQ0N0RBNzg/xMjdFQTFGNzJCMkVENTFFMkQ2ODJGQjk2MDBBRUZERUU0NEVGQjdCLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/WNKvP8hDzHmsocwRAigFpIt7QJXFarIvUnOuwRodUQ8/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9GMzk2M0I3NkQ1QzQ1MzdGODVFRDBGREF/CNUVDMjgzOTAzRTQxMjk2NkU5OUJFNzNFOUVFMDA2NUZDRjAzM0UzLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/ZCtvPWk3W_J-eo1eGQG7i2iUvXRtuyblxElFwnFFp8o/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8zRENERTlDNTMyNEE3OTQwMzAyMDE0M0I/xQTg0MEVENTdFNjM1QzIzNEUxNjU3RjE3MzkyNDZCNzZCRUMwQzE1LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/QDGowmi-rSVPudCY08yhMUl3P1WntirLl_UpsVyr2vA/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9DMkUzQ0JDQjM5QUEzMEMwMzg3QTA0Nzc/3Q0E2QTIwRDIwMzM4NkJFMkYyNTNDOTRDMTc4ODdDRDdEREIxQTY0LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/h2CrMmjnJOEtwk75cdwemtETVGnAChmIgqmKYajWyH8/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS85MTUyM0EwMEM1N0JDRjU4MkQxQzJFMDF/BMjBBREQ5QjFGMjg4QjdGRjhGRTJDMDcyOUQ0NDQwNUNGQ0FBQjI2LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/5zi58jzmoVJtHvGyzx3QqcMq49lZcco9t4t5dIFtslI/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8yMDM2OEE3NEMzQUM5QzI3NzI2QUM0NjZ/BOUM3NDAzRTM5RUIxODQzRDAzQjM2NzJERkMyMTI5NUIzRjBCNDVDLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/TeyTFjlt3aAvYv1jA0WUIkUpd8yTvD3-40re-dnaDO8/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9GRTIxODc0RTU1MDA0OTZEQ0FEOTA5QTI/5MzkxRUE5MDM3NEIzNEI3QUYxMjZDQ0NCRjkzMTM0OTA5Q0JDMDhELmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/rvuTzM2dpbYSHZ_6pIeNeQJX6cEQPGxs_6vaoFNxuHA/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8zMzk0OUIxRjcwRkJERkM1ODIwOTRBQUI/3MzgwM0JDN0MwRjQ0ODNCOUM4MDBEN0E2OTAxMTYwRDQ4OEQ3NTI0LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/PW_Lkr4xhrrjey9Rk_a1bb-E9xzal2KDoUo55BDYNi0/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS84QTE3NEVBQ0M1NTI0N0FBMEMzQTA0QUV/BMDBDRTlCRDNDNEFFRDVBNjQwNTI3OTM2MUVCNTA5OEQ2QTUwNDQ3LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/XOzLRUpvNsuE_RaWCQ_e27cZ8eeepwvb8Qzul7LvgXs/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9DQkY3Qzk0QzFEOTVEMjM1QjUzOTBBM0F/CMzEwNjlDODEwMzRCOEM2RDA0RDlCQTFFNzZCMDc0OUI1ODU2MjFGLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/K-9OZmsvjZmDh0SyorOtsQH0NcE1MRwFQ-B75ecLxeA/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9GRjhGRURBOEVCRjQ2NDM3NTc4OUEyQ0R/CMTk0QkUzQjU5ODYzQTJFQkU5NzUzOEE0NjNGNDYxODAzQjJCNkExLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/6-w8jxQmhmNaH51Yp-kwCzYZFV_3m3vMB7K7BgMX1RQ/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9BNkIxRjhEODE4MEU4MjhENjRERjg0NUI/4QzExMzk5OTE5QkQxRDlBRDU0OURGNUJFNkJGMTkzQjUwREQxNUI4LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/g3NM5HpsO5xyC-IPEWjMQ5YQ4AOVw6R-y2yjTE3ZRKc/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8zNTIzRkM5QUFDMEI4Mjc1QUQ1QTdBQUZ/EMUQ2REI1MUI3MzNGREFDNERFNTI3Q0Q0RDM4MEQ4NzcwQTdFMjcyLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/qKbEGOVbmCn7ZLsDoibFUZqZ3B48DvaLIymMKVDK9ng/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8yMzBDNEMyMTI1MzZFOUEzOTczQ0NFM0V/FQTkxOUVCQTgwOTJFNDU0RDUyQzZERDgzQ0FGMTBDQkQyQUQ3RjQ3LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/qrM2aqRpgzQ1THSc8wlCsiUILvyZOqRFrrtTIrYZZu0/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS85MDlGRjUwQkExNDRDQzY3NzI0QkVFNTh/BODlDMjI0NzAwM0EwRENBMTdCQjc3MTZGNUJDNTlDRkFCMURFNUNFLmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/NiRi-q61ATdKgNJEf9ppfmLYALVBchUGvVyhm29t2wk/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS9CMzVEOEFERkM0MDdDMDkwRDNDMkZGRTY/yRjA4MkY0QTU5RDc4NDg0RTA2QzQzODU2RjkxMEEyMkMwRjExQkY4LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" + }, + { + "name": "foo", + "quantity": "1 кг", + "price": 8.99, + "oldPrice": 10.99, + "category": "НИСКА цена, ВИСОКО качество", + "picUrl": "https://imgproxy-retcat.assets.schwarz/o7kEur0Tz_4Vlesb5sD6mlIETCs2pGnT3uRy2ZAAo8A/sm:1/w:427/h:320/cz/M6Ly9wcm9kLWNhd/GFsb2ctbWVkaWEvYmcvMS8zMTM5NDhCMThFNDFBNDA5RjZBNjlBRjE/5NEY2MzVGN0Y1MUVCNTNDQTU3QjY2QjE2MTVFQkJGOEFEOTQwQzU1LmpwZw.jpg", + "validFrom": "1993-07-28", + "validUntil": "2023-11-04" } ] \ No newline at end of file diff --git a/src/test/resources/extractors/lidl/input-single.html b/src/test/resources/extractors/lidl/input-single.html new file mode 100644 index 0000000..275e490 --- /dev/null +++ b/src/test/resources/extractors/lidl/input-single.html @@ -0,0 +1,1008 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Багета Рустик - Lidl. Верига от любими продукти. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+
Верига от любими продукти + +
+
+ + + +
+
+
+
+
+
+
Този + браузър не е актуален +
+ За да се визуализира правилно сайта, моля ползвай следните браузъри. + + ВниманиеОбнови версията на браузъра си. Ако не го направиш някои функционалности може и да не работят коректно. +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ + + + +
+
+
+
+
+
+
+

+ Rendered: 2023-11-03T23:03:30.000Z +

+
+
+
+ +
+
+
+
+
+

+ Багета Рустик

+
+
+
    +
  • По френска рецепта
  • +
+
+ +
+
+
+
+
+
+
+
+
+ -50% +
+
+
+ + + 1,99 лв + + +
+
+
+ 0,99 +
+
+ * +
+
+ лв +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Характеристики +

+
+
    +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/extractors/lidl/input.html b/src/test/resources/extractors/lidl/input.html index d19a66b..efa600a 100644 --- a/src/test/resources/extractors/lidl/input.html +++ b/src/test/resources/extractors/lidl/input.html @@ -1,6128 +1,8320 @@ - - - - - - - - - - - - - - - - - - - - - - Двойно повече на същата цена - - www.lidl.bg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - - - - - - - - - - -
- - - - - -
- -
- - - - -
- - - -
- - - - - -
- - - - - - -
- -
- -
- - - - -
- - - - - -
- - - Актуални - - - -
- - - - -
- -
- - -
-
- -
- - - - - - - - - - - -
- - - -
- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - -
- - - - - -
-
- - - - -
- - - - - - - - - - - - - - - - - - - - - product.getTitle() - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + НИСКА цена, ВИСОКО качество + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+ + + + +
+
+
+
+
Верига от любими продукти + +
+
+ + - - +
+ +
+
+
+
+
+
+
+
+ Този браузър не е актуален +
+ За да се визуализира правилно сайта, моля ползвай следните браузъри. + + ВниманиеОбнови версията на браузъра си. Ако не го направиш някои функционалности може и да не работят коректно. +
+ +
- - +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ + + +
- - -
- - - - -
-
- - -
- - -
+ + + +
+
+
+
+ diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 17e1170..8efea50 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -3,4 +3,7 @@ + + + \ No newline at end of file