{
  "version": "1.0.0",
  "exported_at": "2026-06-02T12:00:00.000Z",
  "project": {
    "name": "Zalando Product Scraper",
    "description": "Best-effort Zalando.it product listing scraper equivalent to the Octoparse template. It searches preset keywords (borsette, occhiali da sole, cappotti), extracts keyword, title, product URL, brand, current price, previous price, discount, delivery label, and image URL, then follows the listing Next-page link when available. Output is appended across all keyword URLs and pagination pages. The attached sample product URLs were obsolete/404, so resilient listing selectors and JS columns are used.",
    "color": "bg-[#ff6900]",
    "template_id": "ai-generated"
  },
  "blocks": [
    {
      "block_id": "set-window-size-1",
      "block_type": "process",
      "title": "Set Window Size",
      "description": "Set browser viewport size",
      "position_x": 100,
      "position_y": 240,
      "config": {
        "width": 1920,
        "height": 1080,
        "color": "bg-[#4589ff]"
      }
    },
    {
      "block_id": "navigate-1",
      "block_type": "process",
      "title": "Navigate",
      "description": "Go to a URL",
      "position_x": 460,
      "position_y": 240,
      "config": {
        "urls": [
          "https://www.zalando.it/catalogo/?q=borsette",
          "https://www.zalando.it/catalogo/?q=occhiali%20da%20sole",
          "https://www.zalando.it/catalogo/?q=cappotti"
        ],
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "wait-for-page-load-1",
      "block_type": "process",
      "title": "Wait for Page Load",
      "description": "Wait for page to finish loading",
      "position_x": 820,
      "position_y": 240,
      "config": {
        "timeout": 45
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 1180,
      "position_y": 240,
      "config": {
        "selector": "article a[href*='.html']",
        "timeout": 45,
        "visible": true
      }
    },
    {
      "block_id": "scroll-1",
      "block_type": "process",
      "title": "Scroll",
      "description": "Scroll page or element",
      "position_x": 1540,
      "position_y": 240,
      "config": {
        "direction": "down",
        "amount": 1200
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 1900,
      "position_y": 240,
      "config": {
        "duration": 2
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 2260,
      "position_y": 240,
      "config": {
        "rowSelector": "article",
        "fileName": "crawler_prodotti_zalando.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "columns": [
          {
            "name": "parola_chiave",
            "selector": "new URLSearchParams(location.search).get('q') || ''",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "titolo",
            "selector": "(() => { const clean = t => (t || '').replace(/\\s+/g, ' ').trim(); const bad = /€|%|Consegna|Sponsorizzato|Preferiti|Taglie|Disponibile|Aggiungi/i; const texts = Array.from(ROW.querySelectorAll('h3,h2,span,[data-testid]')).map(e => clean(e.textContent)).filter(t => t && !bad.test(t)); const brand = texts[0] || ''; const a = ROW.querySelector('a[href*=\".html\"]'); const aria = clean(a ? a.getAttribute('aria-label') : ''); return texts[1] || aria.replace(brand, '').trim() || texts[0] || ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "pagina_url",
            "selector": "(() => { const a = ROW.querySelector('a[href*=\".html\"]'); return a ? new URL(a.getAttribute('href'), location.href).href : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "brand",
            "selector": "(() => { const clean = t => (t || '').replace(/\\s+/g, ' ').trim(); const bad = /€|%|Consegna|Sponsorizzato|Preferiti|Taglie|Disponibile|Aggiungi/i; const texts = Array.from(ROW.querySelectorAll('h3,h2,span,[data-testid]')).map(e => clean(e.textContent)).filter(t => t && !bad.test(t)); return texts[0] || ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "prezzo_attuale",
            "selector": "(() => { const clean = t => (t || '').replace(/\\s+/g, ' ').trim(); const prices = Array.from(new Set(Array.from(ROW.querySelectorAll('span')).map(e => clean(e.textContent)).filter(t => /€/.test(t) && t.length < 35))); if (prices.length) return prices[0]; const fallback = clean(ROW.textContent).match(/\\d+[,.]\\d{2}\\s*€/); return fallback ? fallback[0] : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "prezzo_prima",
            "selector": "(() => { const clean = t => (t || '').replace(/\\s+/g, ' ').trim(); const prices = Array.from(new Set(Array.from(ROW.querySelectorAll('span')).map(e => clean(e.textContent)).filter(t => /€/.test(t) && t.length < 35))); return prices[1] || ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "sconto",
            "selector": "(() => { const clean = t => (t || '').replace(/\\s+/g, ' ').trim(); const texts = Array.from(ROW.querySelectorAll('span')).map(e => clean(e.textContent)); const hit = texts.find(t => /-\\s*\\d+\\s*%/.test(t)); if (hit) return hit.match(/-\\s*\\d+\\s*%/)[0]; const fallback = clean(ROW.textContent).match(/-\\s*\\d+\\s*%/); return fallback ? fallback[0] : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "consegna",
            "selector": "(() => { const clean = t => (t || '').replace(/\\s+/g, ' ').trim(); const texts = Array.from(ROW.querySelectorAll('span,div')).map(e => clean(e.textContent)); const hit = texts.find(t => /Consegna|Da lunga distanza|Spedizione/i.test(t) && t.length < 80); return hit || ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "img_url",
            "selector": "(() => { const img = ROW.querySelector('img'); return img ? (img.currentSrc || img.src || img.getAttribute('src') || '') : ''; })()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "element-exists-1",
      "block_type": "process",
      "title": "Element Exists",
      "description": "Check if element exists",
      "position_x": 2620,
      "position_y": 240,
      "config": {
        "selector": "a[rel='next'], a[aria-label*='successiva' i]:not([aria-disabled='true']), a[title*='successiva' i]:not([aria-disabled='true'])"
      }
    },
    {
      "block_id": "click-1",
      "block_type": "process",
      "title": "Click",
      "description": "Click on element",
      "position_x": 2980,
      "position_y": 560,
      "config": {
        "selector": "a[rel='next'], a[aria-label*='successiva' i]:not([aria-disabled='true']), a[title*='successiva' i]:not([aria-disabled='true'])",
        "timeout": 15
      }
    },
    {
      "block_id": "wait-for-page-load-2",
      "block_type": "process",
      "title": "Wait for Page Load",
      "description": "Wait for page to finish loading",
      "position_x": 3340,
      "position_y": 560,
      "config": {
        "timeout": 45
      }
    },
    {
      "block_id": "wait-for-element-2",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 3700,
      "position_y": 560,
      "config": {
        "selector": "article a[href*='.html']",
        "timeout": 45,
        "visible": true
      }
    },
    {
      "block_id": "scroll-2",
      "block_type": "process",
      "title": "Scroll",
      "description": "Scroll page or element",
      "position_x": 4060,
      "position_y": 560,
      "config": {
        "direction": "down",
        "amount": 1200
      }
    },
    {
      "block_id": "sleep-2",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 4420,
      "position_y": 560,
      "config": {
        "duration": 2
      }
    },
    {
      "block_id": "loop-continue-1",
      "block_type": "process",
      "title": "Loop Continue",
      "description": "Continue multi-input loop",
      "position_x": 2620,
      "position_y": 880,
      "config": {}
    }
  ],
  "connections": [
    {
      "from_block_id": "set-window-size-1",
      "from_connector_id": "right",
      "to_block_id": "navigate-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "navigate-1",
      "from_connector_id": "right",
      "to_block_id": "wait-for-page-load-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-page-load-1",
      "from_connector_id": "right",
      "to_block_id": "wait-for-element-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-element-1",
      "from_connector_id": "right",
      "to_block_id": "scroll-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "scroll-1",
      "from_connector_id": "right",
      "to_block_id": "sleep-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-1",
      "from_connector_id": "right",
      "to_block_id": "structured-export-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "structured-export-1",
      "from_connector_id": "right",
      "to_block_id": "element-exists-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-exists-1",
      "from_connector_id": "true",
      "to_block_id": "click-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-exists-1",
      "from_connector_id": "false",
      "to_block_id": "loop-continue-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "click-1",
      "from_connector_id": "right",
      "to_block_id": "wait-for-page-load-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-page-load-2",
      "from_connector_id": "right",
      "to_block_id": "wait-for-element-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-element-2",
      "from_connector_id": "right",
      "to_block_id": "scroll-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "scroll-2",
      "from_connector_id": "right",
      "to_block_id": "sleep-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-2",
      "from_connector_id": "right",
      "to_block_id": "structured-export-1",
      "to_connector_id": "left"
    }
  ],
  "canvas_elements": [
    {
      "id": "group-entry",
      "element_type": "group",
      "title": "Entry & Setup",
      "color": "#4589ff",
      "position_x": 28,
      "position_y": 136,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "set-window-size-1"
        ]
      }
    },
    {
      "id": "group-load",
      "element_type": "group",
      "title": "Page Load",
      "color": "#08bdba",
      "position_x": 388,
      "position_y": 136,
      "width": 4280,
      "height": 616,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "wait-for-element-1",
          "sleep-1",
          "wait-for-page-load-2",
          "wait-for-element-2",
          "sleep-2"
        ]
      }
    },
    {
      "id": "group-interaction",
      "element_type": "group",
      "title": "Interaction",
      "color": "#a56eff",
      "position_x": 1468,
      "position_y": 136,
      "width": 2840,
      "height": 616,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "scroll-1",
          "scroll-2"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 2188,
      "position_y": 136,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "structured-export-1"
        ]
      }
    },
    {
      "id": "group-pagination",
      "element_type": "group",
      "title": "Pagination Loop",
      "color": "#ff832b",
      "position_x": 2548,
      "position_y": 136,
      "width": 680,
      "height": 936,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "element-exists-1",
          "click-1",
          "loop-continue-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Best-effort Zalando.it product listing scraper equivalent to the Octoparse template. It searches preset keywords (borsette, occhiali da sole, cappotti), extracts keyword, title, product URL, brand, current price, previous price, discount, delivery label, and image URL, then follows the listing Next-page link when available. Output is appended across all keyword URLs and pagination pages. The attached sample product URLs were obsolete/404, so resilient listing selectors and JS columns are used.",
      "color": "#f1c21b",
      "position_x": 80,
      "position_y": 20,
      "width": 480,
      "height": 160,
      "z_index": 22,
      "data": {}
    },
    {
      "id": "note-block-structured-export-1",
      "element_type": "note",
      "title": "Note: Structured Export",
      "content": "Structured export with JS columns (parola_chiave, titolo, pagina_url, brand, prezzo_attuale). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 2460,
      "position_y": 220,
      "width": 340,
      "height": 132,
      "z_index": 22,
      "data": {
        "block_id": "structured-export-1"
      }
    },
    {
      "id": "note-block-element-exists-1",
      "element_type": "note",
      "title": "Note: Element Exists",
      "content": "Condition block: checks `a[rel='next'], a[aria-label*='successiva' i]:not([aria-disabled='true']), a[title*='successiva' i]:not([aria-disabled='t`. True / False branches control which path runs next. Keep enough space between branches so both connector lines are visible.",
      "color": "#ee5396",
      "position_x": 2820,
      "position_y": 220,
      "width": 340,
      "height": 170,
      "z_index": 22,
      "data": {
        "block_id": "element-exists-1"
      }
    },
    {
      "id": "note-block-click-1",
      "element_type": "note",
      "title": "Note: Click",
      "content": "Pagination click — add waits after this block; the page reloads asynchronously.",
      "color": "#ee5396",
      "position_x": 3180,
      "position_y": 540,
      "width": 316,
      "height": 106,
      "z_index": 22,
      "data": {
        "block_id": "click-1"
      }
    },
    {
      "id": "note-block-loop-continue-1",
      "element_type": "note",
      "title": "Note: Loop Continue",
      "content": "Loop Continue advances a multi-URL or multi-text loop. Place at the end of the loop body with a clear back-edge to the loop start.",
      "color": "#ee5396",
      "position_x": 2820,
      "position_y": 860,
      "width": 340,
      "height": 123,
      "z_index": 22,
      "data": {
        "block_id": "loop-continue-1"
      }
    }
  ]
}