{
  "version": "1.0.0",
  "exported_at": "2026-06-02T14:40:00.000Z",
  "project": {
    "name": "Idealo Price Comparison Scraper",
    "description": "Scrapes Idealo product price-comparison offer pages. Uses a multi-URL navigation loop so users can add multiple Idealo OffersOfProduct/listing URLs; each page's offer rows are appended to one CSV. No separate in-page pagination was detected in the analyzed Idealo offer page. Uses row-scoped JavaScript to handle Idealo's dynamic offer markup, payment icons, shop links, ratings, delivery, and return text.",
    "color": "bg-[#4589ff]",
    "template_id": "ai-generated"
  },
  "blocks": [
    {
      "block_id": "set-window-size-1",
      "block_type": "process",
      "title": "Set Window Size",
      "description": "Set browser window dimensions",
      "position_x": 120,
      "position_y": 220,
      "config": {
        "width": 1920,
        "height": 1080,
        "color": "bg-[#4589ff]"
      }
    },
    {
      "block_id": "navigate-1",
      "block_type": "process",
      "title": "Navigate",
      "description": "Go to a URL",
      "position_x": 456,
      "position_y": 220,
      "config": {
        "urls": [
          "https://www.idealo.de/preisvergleich/OffersOfProduct/5607478_-hefe-weissbier-naturtrueb-paulaner.html",
          "https://www.idealo.de/preisvergleich/OffersOfProduct/202277837_-galaxy-s23-samsung.html"
        ],
        "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": 792,
      "position_y": 220,
      "config": {
        "timeout": 30,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 1128,
      "position_y": 220,
      "config": {
        "selector": ".productOffers-listItem",
        "timeout": 30,
        "visible": true,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 1464,
      "position_y": 220,
      "config": {
        "rowSelector": ".productOffers-listItem",
        "fileName": "idealo_preisvergleich_scraper_verified.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "color": "bg-[#42be65]",
        "columns": [
          {
            "name": "Produkt_url",
            "selector": "location.href",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Produkt_allgemeiner_Titel",
            "selector": "(() => { const el = document.querySelector('h1#oopStage-title, h1.oopStage-title, h1'); return el ? el.textContent.replace(/\\s+/g, ' ').trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Produkt_Titel",
            "selector": "(() => { const selectors = ['[class*=OfferTitle]', '[class*=offerTitle]', '[class*=OfferName]', '[class*=offerName]', '[class*=OfferDescription]', '[class*=offerDescription]', '.productOffers-listItemOfferTitle', '.productOffers-listItemOfferName', '.productOffers-listItemOfferDescription']; for (const s of selectors) { const el = ROW.querySelector(s); const t = el && el.textContent ? el.textContent.replace(/\\s+/g, ' ').trim() : ''; if (t && !/^Zum Shop$/i.test(t)) return t; } const lines = (ROW.innerText || ROW.textContent || '').split(/\\n+/).map(x => x.replace(/\\s+/g, ' ').trim()).filter(Boolean); const reject = /^(Zum Shop|Shop|Preis|Preis & Versand|Zahlungsarten|Lieferung|Lieferung:|Rücksendung|Rechnung|Lastschrift|Vorkasse|Auf Lager|Marktplatz|Verkauf durch|Günstigster Gesamtpreis|Günstigster|Gesamtpreis)$/i; for (const line of lines) { if (reject.test(line)) continue; if (/^\\d{1,5}(?:[.,]\\d{2})?\\s*€/.test(line)) continue; if (/^\\d+[,.]\\d+\\s*€?\\s*\\/?\\s*(Liter|GB|kg|Stück|St\\.)/i.test(line)) continue; if (/^\\d+[,.]\\d+$/.test(line)) continue; if (/^\\d+\\s*(Meinungen|Bewertungen)$/i.test(line)) continue; return line; } return ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Preis",
            "selector": "(() => { const txt = ROW.textContent.replace(/\\s+/g, ' ').trim(); const m = txt.match(/\\d{1,5}(?:[.,]\\d{2})\\s*€/); return m ? m[0] : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Preis_inkl_Versand",
            "selector": "(() => { const txt = ROW.textContent.replace(/\\s+/g, ' ').trim(); const m = txt.match(/\\d{1,5}(?:[.,]\\d{2})\\s*€\\s*inkl\\.?\\s*Versand/i); return m ? m[0] : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Zahlungsweise",
            "selector": "(() => Array.from(ROW.querySelectorAll('img[src*=payment-icons], img[data-src*=payment-icons]')).map(img => img.getAttribute('src') || img.getAttribute('data-src')).filter(Boolean).map(src => new URL(src, location.href).href).join(' '))()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Lieferzeit",
            "selector": "(() => { const lines = (ROW.innerText || ROW.textContent || '').split(/\\n+/).map(x => x.replace(/\\s+/g, ' ').trim()).filter(Boolean); for (const line of lines) { if (/^Auf Lager$/i.test(line)) return line; } const full = lines.join(' | '); let m = full.match(/Lieferung:?\\s*(bis\\s*[^|]{1,35}(?:DHL|DPD|Hermes|GLS|UPS|GoGreen)?)/i); if (m) return ('Lieferung: ' + m[1]).replace(/\\s+/g, ' ').trim(); m = full.match(/(bis\\s*(?:Mo|Di|Mi|Do|Fr|Sa|So)\\.?\\s*\\d{1,2}\\.\\d{1,2}\\.?[^|]{0,30})/i); if (m) return ('Lieferung: ' + m[1]).replace(/\\s+/g, ' ').trim(); for (let i = 0; i < lines.length; i++) { if (/^Lieferung:?$/i.test(lines[i])) { const next = lines.slice(i + 1, i + 8).find(l => /^bis\\s+/i.test(l) || /DHL|DPD|Hermes|GLS|UPS|GoGreen/i.test(l)); return next ? ('Lieferung: ' + next).replace(/\\s+/g, ' ').trim() : 'Lieferung:'; } } return ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Umstand_von_Rücksendung",
            "selector": "(() => { const lines = (ROW.innerText || ROW.textContent || '').split(/\\n+/).map(x => x.replace(/\\s+/g, ' ').trim()).filter(Boolean); const found = lines.find(line => /^Rücksendung/i.test(line)); return found ? found.trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Webseite_url",
            "selector": "(() => { const links = Array.from(ROW.querySelectorAll('a[href]')); const byText = links.find(a => /Zum\\s*Shop/i.test(a.textContent || '')); const byHref = links.find(a => /GoProduct|redirect|shop|offer/i.test(a.href || '')); const chosen = byText || byHref || links[0]; return chosen && chosen.href ? chosen.href : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Kundenbewertung",
            "selector": "(() => { const cls = Array.from(ROW.querySelectorAll('[class*=rating-starsWrapper-]')).map(e => e.className || '').join(' '); const cm = cls.match(/rating-starsWrapper-(\\d+)/); if (cm) return cm[1]; const txt = ROW.textContent.replace(/\\s+/g, ' '); const dm = txt.match(/\\b([0-5],[0-9])\\b/); if (dm) return String(Math.round(parseFloat(dm[1].replace(',', '.')) * 20)); return ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Anzahl_der_Bewertungen",
            "selector": "(() => { const txt = ROW.textContent.replace(/\\s+/g, ' ').trim(); const m = txt.match(/([\\d.]+)\\s*(Meinungen|Bewertungen)/i); if (m) return m[1] + ' ' + m[2]; const dm = txt.match(/\\b[0-5],[0-9]\\s+([\\d.]{2,})\\b/); return dm ? dm[1] : ''; })()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "loop-continue-1",
      "block_type": "process",
      "title": "Loop Continue",
      "description": "Continue multi-input loop",
      "position_x": 1800,
      "position_y": 220,
      "config": {
        "color": "bg-[#ff832b]"
      }
    }
  ],
  "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": "structured-export-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "structured-export-1",
      "from_connector_id": "right",
      "to_block_id": "loop-continue-1",
      "to_connector_id": "left"
    }
  ],
  "canvas_elements": [
    {
      "id": "group-entry",
      "element_type": "group",
      "title": "Entry & Setup",
      "color": "#4589ff",
      "position_x": 48,
      "position_y": 116,
      "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": 384,
      "position_y": 116,
      "width": 992,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "wait-for-element-1"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 1392,
      "position_y": 116,
      "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": 1728,
      "position_y": 116,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "loop-continue-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Scrapes Idealo product price-comparison offer pages. Uses a multi-URL navigation loop so users can add multiple Idealo OffersOfProduct/listing URLs; each page's offer rows are appended to one CSV. No separate in-page pagination was detected in the analyzed Idealo offer page. Uses row-scoped JavaScript to handle Idealo's dynamic offer markup, payment icons, shop links, ratings, delivery, and return text.",
      "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 (Produkt_url, Produkt_allgemeiner_Titel, Produkt_Titel, Preis, Preis_inkl_Versand). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 1664,
      "position_y": 200,
      "width": 340,
      "height": 140,
      "z_index": 22,
      "data": {
        "block_id": "structured-export-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": 2000,
      "position_y": 200,
      "width": 340,
      "height": 123,
      "z_index": 22,
      "data": {
        "block_id": "loop-continue-1"
      }
    }
  ]
}