{
  "version": "1.0.0",
  "exported_at": "2026-06-02T18:00:00.000Z",
  "project": {
    "name": "11st Product List Scraper",
    "description": "Scrapes 11st product listing/search URLs for keyword, result count, product image, product name, product URL, 11st lowest-price label, price, seller, seller URL, delivery, and arrival date. Uses click-next pagination and appends each page to one CSV until no enabled Next button is found. Best-effort template: 11st may show CAPTCHA or block product pages, which may require manual solving in the built-in browser.",
    "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": 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": {
        "url": "https://search.11st.co.kr/pc/total-search?kwd=%25EC%2597%25AC%25EC%2584%25B1%25EC%2588%258F%25ED%258C%25A8%25EB%2594%25A9&tabId=TOTAL_SEARCH",
        "color": "bg-[#4589ff]"
      }
    },
    {
      "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": 30,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "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": "li:has(a[href*=\"/products/\"])",
        "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": 1540,
      "position_y": 240,
      "config": {
        "rowSelector": "li:has(a[href*=\"/products/\"])",
        "fileName": "11st-product-list-scraper.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "color": "bg-[#42be65]",
        "columns": [
          {
            "name": "search_keyword",
            "selector": "(() => { const params = new URLSearchParams(location.search); const raw = params.get('kwd') || document.querySelector('input[name=\"kwd\"], input.search_text, input[title*=\"검색\"]')?.value || ''; try { return decodeURIComponent(raw); } catch (e) { return raw; } })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "search_result_count",
            "selector": "(() => { const text = document.body.innerText || ''; const candidates = [ /검색결과\\s*([\\d,]+)/, /총\\s*([\\d,]+)\\s*개/, /([\\d,]+)\\s*개의\\s*상품/ ]; for (const re of candidates) { const m = text.match(re); if (m) return m[1]; } const el = document.querySelector('[class*=\"total\"], [class*=\"count\"], [class*=\"result\"]'); return el ? (el.textContent.match(/[\\d,]+/) || [''])[0] : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "product_image",
            "selector": "(() => { const img = ROW.querySelector('img'); if (!img) return ''; return img.currentSrc || img.src || img.getAttribute('data-src') || img.getAttribute('data-original') || ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "product",
            "selector": "(() => { const imgAlt = ROW.querySelector('a[href*=\"/products/\"] img')?.getAttribute('alt'); if (imgAlt && imgAlt.trim()) return imgAlt.trim(); const nameEl = ROW.querySelector('.store_product_name, [class*=\"product_name\"], [class*=\"prd_name\"], [class*=\"name\"], [class*=\"title\"], a[href*=\"/products/\"]'); return nameEl ? nameEl.textContent.replace(/\\s+/g, ' ').trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "product_link",
            "selector": "(() => { const a = ROW.querySelector('a[href*=\"/products/\"]'); return a ? a.href : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "site_lowest_price",
            "selector": "(() => { const text = ROW.innerText || ''; const m = text.match(/11번가\\s*최저가/); return m ? m[0] : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "price",
            "selector": "(() => { const preferred = ROW.querySelector('.store_product_new_price, [class*=\"new_price\"], [class*=\"sale_price\"], [class*=\"price\"]'); const text = preferred ? preferred.textContent : ROW.innerText; const patterns = [/판매가\\s*([\\d,]+)\\s*원/, /최저가\\s*([\\d,]+)\\s*원/, /([\\d,]+)\\s*원/]; for (const re of patterns) { const m = (text || '').match(re); if (m) return m[1]; } return ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "seller",
            "selector": "(() => { const sellerLink = ROW.querySelector('a[href*=\"shop.11st.co.kr/stores\"], a[href*=\"/stores/\"]'); if (sellerLink && sellerLink.textContent.trim()) return sellerLink.textContent.replace(/\\s+/g, ' ').trim(); const info = ROW.querySelector('[data-nickname]'); if (info) return info.getAttribute('data-nickname') || ''; const sellerEl = ROW.querySelector('[class*=\"seller\"], [class*=\"store\"], [class*=\"mall\"]'); return sellerEl ? sellerEl.textContent.replace(/\\s+/g, ' ').trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "seller_link",
            "selector": "(() => { const a = ROW.querySelector('a[href*=\"shop.11st.co.kr/stores\"], a[href*=\"/stores/\"]'); return a ? a.href : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "delivery",
            "selector": "(() => { const text = ROW.innerText.replace(/\\s+/g, ' '); const m = text.match(/무료배송|배송비\\s*[\\d,]+원|배송비\\s*무료/); return m ? m[0] : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "arrival_date",
            "selector": "(() => { const text = ROW.innerText.replace(/\\s+/g, ' '); const patterns = [/\\d{1,2}\\/\\d{1,2}\\([^)]*\\)\\s*도착/, /내일도착[^\\s]*/, /오늘도착[^\\s]*/, /[\\d]{1,2}\\/[^\\s]{1,10}\\s*도착/, /[^\\s]{1,12}\\s*도착/]; for (const re of patterns) { const m = text.match(re); if (m) return m[0]; } return ''; })()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "element-exists-1",
      "block_type": "process",
      "title": "Element Exists",
      "description": "Check if element exists",
      "position_x": 1900,
      "position_y": 240,
      "config": {
        "selector": "//*[self::a or self::button][(contains(@class,'next') or contains(normalize-space(.),'다음')) and not(contains(@class,'disabled')) and not(contains(@class,'disable')) and not(@disabled)]",
        "color": "bg-[#ff832b]"
      }
    },
    {
      "block_id": "click-1",
      "block_type": "process",
      "title": "Click",
      "description": "Click on element",
      "position_x": 2260,
      "position_y": 620,
      "config": {
        "selector": "//*[self::a or self::button][(contains(@class,'next') or contains(normalize-space(.),'다음')) and not(contains(@class,'disabled')) and not(contains(@class,'disable')) and not(@disabled)]",
        "timeout": 10,
        "color": "bg-[#ff832b]"
      }
    },
    {
      "block_id": "wait-for-page-load-2",
      "block_type": "process",
      "title": "Wait for Page Load",
      "description": "Wait for page to finish loading",
      "position_x": 2620,
      "position_y": 620,
      "config": {
        "timeout": 30,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 2980,
      "position_y": 620,
      "config": {
        "duration": 2,
        "color": "bg-[#ff832b]"
      }
    },
    {
      "block_id": "wait-for-element-2",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 3340,
      "position_y": 620,
      "config": {
        "selector": "li:has(a[href*=\"/products/\"])",
        "timeout": 30,
        "visible": true,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "end-1",
      "block_type": "output",
      "title": "End",
      "description": "Terminate execution flow",
      "position_x": 1900,
      "position_y": 940,
      "config": {
        "color": "bg-[#8d8d8d]"
      }
    }
  ],
  "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": "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": "end-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": "sleep-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-1",
      "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": "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": 3200,
      "height": 676,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "wait-for-element-1",
          "wait-for-page-load-2",
          "sleep-1",
          "wait-for-element-2"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 1468,
      "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": 1828,
      "position_y": 136,
      "width": 680,
      "height": 676,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "element-exists-1",
          "click-1"
        ]
      }
    },
    {
      "id": "group-control",
      "element_type": "group",
      "title": "Control Flow",
      "color": "#8d8d8d",
      "position_x": 1828,
      "position_y": 836,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "end-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Scrapes 11st product listing/search URLs for keyword, result count, product image, product name, product URL, 11st lowest-price label, price, seller, seller URL, delivery, and arrival date. Uses click-next pagination and appends each page to one CSV until no enabled Next button is found. Best-effort template: 11st may show CAPTCHA or block product pages, which may require manual solving in the built-in browser.",
      "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 (search_keyword, search_result_count, product_image, product, product_link). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 1740,
      "position_y": 220,
      "width": 340,
      "height": 138,
      "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 `//*[self::a or self::button][(contains(@class,'next') or contains(normalize-space(.),'다음')) and not(contains(@class,'dis`. True / False branches control which path runs next. Keep enough space between branches so both connector lines are visible.",
      "color": "#ee5396",
      "position_x": 2100,
      "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": "Uses XPath `//*[self::a or self::button][(contains(@class,'next') or contains(normalize-space(.),'다음')) and not(`. XPath breaks easily if DOM structure changes.",
      "color": "#ee5396",
      "position_x": 2460,
      "position_y": 600,
      "width": 340,
      "height": 133,
      "z_index": 22,
      "data": {
        "block_id": "click-1"
      }
    }
  ]
}