{
  "version": "1.0.0",
  "exported_at": "2026-06-02T19:15:00.000Z",
  "project": {
    "name": "Chefkochde Rezepte Detail Scraper",
    "description": "Scrapes Chefkoch.de recipe detail pages equivalent to the Octoparse Chefkoch recipe detail template: recipe URL, title, description, rating, review/comment counts, cooking times, difficulty, publication date, ingredients, preparation text, author profile, and related recipes. Uses a multi-URL navigation loop for multiple recipe detail URLs; no on-page pagination exists on individual recipe detail pages. Includes best-effort visible cookie consent handling and may require manual CAPTCHA solving if Chefkoch blocks automated access.",
    "color": "bg-[#42be65]",
    "template_id": "ai-generated"
  },
  "blocks": [
    {
      "block_id": "navigate-1",
      "block_type": "process",
      "title": "Navigate",
      "description": "Go to a URL",
      "position_x": 100,
      "position_y": 240,
      "config": {
        "urls": [
          "https://www.chefkoch.de/rezepte/1385291243604216/Bauerntopf.html",
          "https://www.chefkoch.de/rezepte/1342761239096947/Filettopf.html"
        ],
        "color": "bg-[#4589ff]",
        "tags": [
          "chefkoch",
          "recipe-detail",
          "multi-url"
        ]
      }
    },
    {
      "block_id": "wait-for-page-load-1",
      "block_type": "process",
      "title": "Wait for Page Load",
      "description": "Wait for page to finish loading",
      "position_x": 460,
      "position_y": 240,
      "config": {
        "timeout": 30
      }
    },
    {
      "block_id": "element-visible-1",
      "block_type": "process",
      "title": "Element Visible",
      "description": "Check if element is visible",
      "position_x": 820,
      "position_y": 240,
      "config": {
        "selector": ".cmpboxbtnyes, .cmptxt_btn_yes"
      }
    },
    {
      "block_id": "click-1",
      "block_type": "process",
      "title": "Click",
      "description": "Click on element",
      "position_x": 1180,
      "position_y": 240,
      "config": {
        "selector": ".cmpboxbtnyes, .cmptxt_btn_yes",
        "timeout": 10
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 1540,
      "position_y": 240,
      "config": {
        "duration": 1
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 1540,
      "position_y": 560,
      "config": {
        "selector": "h1",
        "timeout": 30,
        "visible": true
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 1900,
      "position_y": 560,
      "config": {
        "rowSelector": "body",
        "fileName": "chefkochde-rezepte-detail-scraper.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "columns": [
          {
            "name": "rezept_url",
            "selector": "window.location.href",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "titel",
            "selector": "ROW.querySelector('h1')?.innerText.trim() || ''",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "beschreibung",
            "selector": "ROW.querySelector('header h1 + p')?.innerText.trim() || document.querySelector('meta[name=\"description\"]')?.content || ''",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "kundenbewertung",
            "selector": "(()=>{const el=ROW.querySelector('.rating-desktop .ds-rating__avg')||ROW.querySelector('.ds-rating__avg');if(!el)return'';const clone=el.cloneNode(true);clone.querySelectorAll('.ds-sr-only').forEach(n=>n.remove());return clone.textContent.replace('/','').trim();})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "anzahl_der_bewertungen",
            "selector": "(()=>{const t=(ROW.querySelector('.rating-desktop .ds-rating__votes')||ROW.querySelector('.ds-rating__votes'))?.textContent||'';return (t.match(/\\d+/)||[''])[0];})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "anzahl_der_kommentare",
            "selector": "(()=>{const t=ROW.querySelector('.comments-link__link .ds-btn__label')?.textContent||'';return (t.match(/\\d+/)||[''])[0];})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "gesamtzeit",
            "selector": "(()=>{const t=ROW.innerText.replace(/\\s+/g,' ');const m=t.match(/Zubereitung\\s+[^0-9]*([0-9][0-9A-Za-zÄÖÜäöüß.\\s]*?)\\s+Gesamtzeit/);return m?m[1].trim():'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "schwierigkeitsgrad",
            "selector": "(()=>{const t=ROW.innerText.replace(/\\s+/g,' ');const m=t.match(/\\b(Simpel|Einfach|Normal|Pfiffig|Schwierig)\\b\\s+Schwierigkeit/i);return m?m[1].charAt(0).toUpperCase()+m[1].slice(1).toLowerCase():'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "veroeffentlichungszeit",
            "selector": "(()=>{const t=ROW.innerText.replace(/\\s+/g,' ');const m=t.match(/Schwierigkeit\\s+[^0-9]*(\\d{1,2}\\.\\d{1,2}\\.\\d{4})\\s+Erstellt am/);return m?m[1].trim():'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "arbeitszeit",
            "selector": "(()=>{const t=ROW.innerText.replace(/\\s+/g,' ');const m=t.match(/Gesamtzeit\\s+[^0-9]*([0-9][0-9A-Za-zÄÖÜäöüß.\\s]*?)\\s+Arbeitszeit/);return m?m[1].trim():'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "details_der_zutaten",
            "selector": "(()=>Array.from(ROW.querySelectorAll('.ds-ingredients-table__tr')).map(tr=>tr.innerText.replace(/\\s+/g,' ').trim()).filter(Boolean).join(' | '))()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "vorstellung_der_zubereitung",
            "selector": "(()=>{const t=ROW.innerText.replace(/\\s+/g,' ');const m=t.match(/Zubereitung\\s+(.+?)\\s+(?:heart_border\\s+)?Speichern\\s+calendar_add/);return m?m[1].trim():'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "rezept_von",
            "selector": "(()=>{const h=Array.from(ROW.querySelectorAll('h2')).find(e=>e.textContent.includes('Rezeptautor'));if(h){let n=h.parentElement;for(let i=0;i<5&&n;i++,n=n.parentElement){const a=n.querySelector('a[href*=\"/user/profil/\"]');if(a)return a.href;}}const t=ROW.innerText.replace(/\\s+/g,' ');const m=t.match(/Erstellt am\\s+(?:profil\\s+)?(.+?)\\s+Rezeptautor/);const name=m?m[1].trim():'';const a=Array.from(ROW.querySelectorAll('a[href*=\"/user/profil/\"]')).find(x=>name&&x.href.toLowerCase().includes(name.toLowerCase()));return a?a.href:'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "rezept_von_name",
            "selector": "(()=>{const t=ROW.innerText.replace(/\\s+/g,' ');const m=t.match(/Erstellt am\\s+(?:profil\\s+)?(.+?)\\s+Rezeptautor/);return m?m[1].trim():'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "weitere_rezepte",
            "selector": "(()=>{const h=Array.from(ROW.querySelectorAll('h2')).find(e=>e.textContent.includes('Weitere Rezepte'));if(!h)return'';let c=h.parentElement;while(c&&c.querySelectorAll('a[href*=\"/rezepte/\"]').length===0)c=c.parentElement;const links=Array.from((c||ROW).querySelectorAll('a[href*=\"/rezepte/\"]')).map(a=>a.href).filter(Boolean);return Array.from(new Set(links)).slice(0,8).join(' | ');})()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "sleep-2",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 2260,
      "position_y": 560,
      "config": {
        "duration": 1
      }
    },
    {
      "block_id": "loop-continue-1",
      "block_type": "process",
      "title": "Loop Continue",
      "description": "Continue multi-input loop",
      "position_x": 2620,
      "position_y": 560,
      "config": {}
    }
  ],
  "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": "element-visible-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-visible-1",
      "from_connector_id": "true",
      "to_block_id": "click-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-visible-1",
      "from_connector_id": "false",
      "to_block_id": "wait-for-element-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "click-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": "sleep-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-2",
      "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": 28,
      "position_y": 136,
      "width": 2480,
      "height": 616,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "sleep-1",
          "wait-for-element-1",
          "sleep-2"
        ]
      }
    },
    {
      "id": "group-pagination",
      "element_type": "group",
      "title": "Pagination Loop",
      "color": "#ff832b",
      "position_x": 748,
      "position_y": 136,
      "width": 2120,
      "height": 616,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "element-visible-1",
          "click-1",
          "loop-continue-1"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 1828,
      "position_y": 456,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "structured-export-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Scrapes Chefkoch.de recipe detail pages equivalent to the Octoparse Chefkoch recipe detail template: recipe URL, title, description, rating, review/comment counts, cooking times, difficulty, publication date, ingredients, preparation text, author profile, and related recipes. Uses a multi-URL navigation loop for multiple recipe detail URLs; no on-page pagination exists on individual recipe detail pages. Includes best-effort visible cookie consent handling and may require manual CAPTCHA solving if Chefkoch blocks automated access.",
      "color": "#f1c21b",
      "position_x": 80,
      "position_y": 20,
      "width": 480,
      "height": 160,
      "z_index": 22,
      "data": {}
    },
    {
      "id": "note-block-element-visible-1",
      "element_type": "note",
      "title": "Note: Element Visible",
      "content": "Condition block: checks `.cmpboxbtnyes, .cmptxt_btn_yes`. True / False branches control which path runs next. Keep enough space between branches so both connector lines are visible.",
      "color": "#ee5396",
      "position_x": 1020,
      "position_y": 220,
      "width": 340,
      "height": 140,
      "z_index": 22,
      "data": {
        "block_id": "element-visible-1"
      }
    },
    {
      "id": "note-block-structured-export-1",
      "element_type": "note",
      "title": "Note: Structured Export",
      "content": "Structured export with JS columns (rezept_url, titel, beschreibung, kundenbewertung, anzahl_der_bewertungen). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 2100,
      "position_y": 540,
      "width": 340,
      "height": 138,
      "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": 2820,
      "position_y": 540,
      "width": 340,
      "height": 123,
      "z_index": 22,
      "data": {
        "block_id": "loop-continue-1"
      }
    }
  ]
}