{
  "version": "1.0.0",
  "exported_at": "2026-06-01T15:35:00.000Z",
  "project": {
    "name": "Kleinanzeigen Scraper Product Listing",
    "description": "Scrapes Kleinanzeigen.de product listing results for Rotweingläser in Berlin. Extracts product URL, image URL, location, announcement time, product title, description, price, and Pro marker. Pagination is implemented with a known URL list for the 6 detected result pages from 143 total results, using navigate.urls[] and loop-continue with CSV append mode. Includes best-effort GDPR/cookie banner dismissal/removal; site anti-bot protections or layout changes may affect execution.",
    "color": "bg-[#4589ff]",
    "template_id": "ai-generated"
  },
  "blocks": [
    {
      "block_id": "navigate-1",
      "block_type": "process",
      "title": "Navigate",
      "description": "Go to a URL",
      "position_x": 120,
      "position_y": 240,
      "config": {
        "urls": [
          "https://www.kleinanzeigen.de/s-berlin/rotweingl%C3%A4ser/k0l3331",
          "https://www.kleinanzeigen.de/s-berlin/seite:2/rotweingl%C3%A4ser/k0l3331",
          "https://www.kleinanzeigen.de/s-berlin/seite:3/rotweingl%C3%A4ser/k0l3331",
          "https://www.kleinanzeigen.de/s-berlin/seite:4/rotweingl%C3%A4ser/k0l3331",
          "https://www.kleinanzeigen.de/s-berlin/seite:5/rotweingl%C3%A4ser/k0l3331",
          "https://www.kleinanzeigen.de/s-berlin/seite:6/rotweingl%C3%A4ser/k0l3331"
        ],
        "color": "bg-[#4589ff]",
        "tags": [
          "entry",
          "kleinanzeigen",
          "paginated-url-list"
        ]
      }
    },
    {
      "block_id": "wait-for-page-load-1",
      "block_type": "process",
      "title": "Wait for Page Load",
      "description": "Wait for page to finish loading",
      "position_x": 480,
      "position_y": 240,
      "config": {
        "timeout": 30,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "inject-javascript-1",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Run custom JavaScript",
      "position_x": 840,
      "position_y": 240,
      "config": {
        "jsCode": "(() => {\n  const clickSelectors = [\n    \"button[id*='accept' i]\",\n    \"button[id*='agree' i]\",\n    \"button[data-testid*='accept' i]\",\n    \"button[aria-label*='accept' i]\",\n    \"button[aria-label*='akzept' i]\",\n    \"button[title*='accept' i]\",\n    \"button[title*='akzept' i]\"\n  ];\n  for (const selector of clickSelectors) {\n    const button = document.querySelector(selector);\n    if (button) {\n      button.click();\n    }\n  }\n\n  const textButtons = Array.from(document.querySelectorAll('button'));\n  const acceptButton = textButtons.find(btn => /^(Alle akzeptieren|Akzeptieren|Accept|Agree|Zustimmen)$/i.test((btn.textContent || '').trim()));\n  if (acceptButton) {\n    acceptButton.click();\n  }\n\n  const overlays = [\n    '#consentBanner',\n    '#gdpr-banner',\n    '.gdpr-cmp',\n    'dialog#gdpr-banner',\n    '[aria-labelledby=\"gdpr-banner-title\"]'\n  ];\n  for (const selector of overlays) {\n    document.querySelectorAll(selector).forEach(el => el.remove());\n  }\n\n  document.body.style.overflow = 'auto';\n  document.documentElement.style.overflow = 'auto';\n  return 'cookie/gdpr overlay dismissed or removed if present';\n})()",
        "waitForCompletion": true,
        "timeout": 10,
        "color": "bg-[#a56eff]"
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 1200,
      "position_y": 240,
      "config": {
        "duration": 1,
        "color": "bg-[#8d8d8d]"
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 1560,
      "position_y": 240,
      "config": {
        "selector": "article.aditem",
        "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": 1920,
      "position_y": 240,
      "config": {
        "rowSelector": "article.aditem",
        "fileName": "kleinanzeigen-scraper-product-listing.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "color": "bg-[#42be65]",
        "columns": [
          {
            "name": "produkt_url",
            "selector": "(() => {\n  const a = ROW.querySelector('.aditem-main--middle h2 a, a[href*=\"/s-anzeige/\"]');\n  return a ? new URL(a.getAttribute('href'), location.origin).href : '';\n})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "image_url",
            "selector": "(() => {\n  const img = ROW.querySelector('.aditem-image img, .imagebox img, .srpimagebox img');\n  const box = ROW.querySelector('.aditem-image, .imagebox, .srpimagebox');\n  const raw = (img && (img.getAttribute('src') || img.getAttribute('data-src') || img.getAttribute('data-imgsrc') || img.currentSrc)) || (box && (box.getAttribute('data-imgsrc') || box.getAttribute('data-src'))) || '';\n  return raw ? new URL(raw, location.origin).href : '';\n})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "standort",
            "selector": ".aditem-main--top--left",
            "attribute": "text"
          },
          {
            "name": "ankuendigungszeit",
            "selector": ".aditem-main--top--right",
            "attribute": "text"
          },
          {
            "name": "produkt_titel",
            "selector": "(() => {\n  const title = ROW.querySelector('.aditem-main--middle h2 a, .aditem-main--middle h2, h2 a, h2');\n  return title ? (title.textContent || '').trim() : '';\n})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "produkt_beschreibung",
            "selector": ".aditem-main--middle--description",
            "attribute": "text"
          },
          {
            "name": "preis",
            "selector": ".aditem-main--middle--price-shipping--price",
            "attribute": "text"
          },
          {
            "name": "pro",
            "selector": "(() => {\n  const candidates = Array.from(ROW.querySelectorAll('.simpletag, .badge, [class*=\"tag\"], [class*=\"pro\"], [class*=\"Pro\"]'));\n  const exact = candidates.map(e => (e.textContent || '').trim()).find(t => /^PRO$/i.test(t));\n  if (exact) return 'PRO';\n  return /(^|\\s)PRO(\\s|$)/.test(ROW.textContent || '') ? 'PRO' : '';\n})()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "loop-continue-1",
      "block_type": "process",
      "title": "Loop Continue",
      "description": "Continue multi-input loop",
      "position_x": 2280,
      "position_y": 240,
      "config": {
        "color": "bg-[#ff832b]"
      }
    }
  ],
  "connections": [
    {
      "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": "inject-javascript-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "inject-javascript-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": "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-load",
      "element_type": "group",
      "title": "Page Load",
      "color": "#08bdba",
      "position_x": 48,
      "position_y": 136,
      "width": 1760,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "sleep-1",
          "wait-for-element-1"
        ]
      }
    },
    {
      "id": "group-interaction",
      "element_type": "group",
      "title": "Interaction",
      "color": "#a56eff",
      "position_x": 768,
      "position_y": 136,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "inject-javascript-1"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 1848,
      "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": 2208,
      "position_y": 136,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "loop-continue-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Scrapes Kleinanzeigen.de product listing results for Rotweingläser in Berlin. Extracts product URL, image URL, location, announcement time, product title, description, price, and Pro marker. Pagination is implemented with a known URL list for the 6 detected result pages from 143 total results, using navigate.urls[] and loop-continue with CSV append mode. Includes best-effort GDPR/cookie banner dismissal/removal; site anti-bot protections or layout changes may affect execution.",
      "color": "#f1c21b",
      "position_x": 80,
      "position_y": 20,
      "width": 480,
      "height": 160,
      "z_index": 22,
      "data": {}
    },
    {
      "id": "note-block-navigate-1",
      "element_type": "note",
      "title": "Note: Navigate",
      "content": "Multi-URL loop over 6 pages. Pair with loop-continue at the end of each iteration.",
      "color": "#ee5396",
      "position_x": 320,
      "position_y": 220,
      "width": 328,
      "height": 107,
      "z_index": 22,
      "data": {
        "block_id": "navigate-1"
      }
    },
    {
      "id": "note-block-inject-javascript-1",
      "element_type": "note",
      "title": "Note: Inject JavaScript",
      "content": "Runs custom JavaScript in the page: `(() => {\n  const clickSelectors = [\n    \"button[id*='accept' i]\",\n    \"button[id*='agree' i]\",\n    \"...` Verify in browser if results are empty.",
      "color": "#ee5396",
      "position_x": 1040,
      "position_y": 220,
      "width": 340,
      "height": 140,
      "z_index": 22,
      "data": {
        "block_id": "inject-javascript-1"
      }
    },
    {
      "id": "note-block-structured-export-1",
      "element_type": "note",
      "title": "Note: Structured Export",
      "content": "Structured export with JS columns (produkt_url, image_url, produkt_titel, pro). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 2120,
      "position_y": 220,
      "width": 340,
      "height": 128,
      "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": 2480,
      "position_y": 220,
      "width": 340,
      "height": 123,
      "z_index": 22,
      "data": {
        "block_id": "loop-continue-1"
      }
    }
  ]
}