{
  "version": "1.0.0",
  "exported_at": "2026-06-01T00:00:00.000Z",
  "project": {
    "name": "Dcinside Scraper",
    "description": "Scrapes DCInside MapleStory gallery listing pages and enriches each visible post with detail-page created time, text content, and attached image URLs. Pagination uses a safe known-URL list for pages 1-3 to avoid infinite traversal on DCInside's very large archive; extend the navigate.urls array to collect additional pages. The extraction waits for the async detail-enrichment script to finish before exporting.",
    "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": 220,
      "config": {
        "urls": [
          "https://gall.dcinside.com/board/lists/?id=maplestory_new&page=1",
          "https://gall.dcinside.com/board/lists/?id=maplestory_new&page=2",
          "https://gall.dcinside.com/board/lists/?id=maplestory_new&page=3"
        ],
        "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": 480,
      "position_y": 220,
      "config": {
        "timeout": 30
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 840,
      "position_y": 220,
      "config": {
        "selector": "tr.ub-content .gall_tit a, table.gall_list tbody tr.ub-content",
        "timeout": 30,
        "visible": true
      }
    },
    {
      "block_id": "inject-javascript-1",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Run custom JavaScript on the page",
      "position_x": 1200,
      "position_y": 220,
      "config": {
        "jsCode": "(async () => { const delay = ms => new Promise(r => setTimeout(r, ms)); const clean = s => (s || '').replace(/\\s+/g, ' ').trim(); const abs = href => { try { return href ? new URL(href, location.href).href : ''; } catch (e) { return ''; } }; const params = new URLSearchParams(location.search); const galleryId = params.get('id') || 'maplestory_new'; const pageNo = params.get('page') || '1'; const old = document.querySelector('#uscraper-dcinside-results'); if (old) old.remove(); const root = document.createElement('div'); root.id = 'uscraper-dcinside-results'; root.setAttribute('data-complete', '0'); root.style.display = 'none'; document.body.appendChild(root); const get = (el, sel) => clean(el.querySelector(sel)?.textContent); let rows = Array.from(document.querySelectorAll('table.gall_list tbody tr.ub-content, tr.ub-content')).filter(row => { const number = clean(row.querySelector('.gall_num')?.textContent || row.getAttribute('data-no') || ''); return /^\\d+$/.test(number) && row.querySelector('.gall_tit'); }); if (rows.length < 2) { const seen = new Set(); rows = Array.from(document.querySelectorAll('.gall_num')).map(el => el.closest('tr')).filter(row => { if (!row || seen.has(row)) return false; seen.add(row); const number = clean(row.querySelector('.gall_num')?.textContent || row.getAttribute('data-no') || ''); return /^\\d+$/.test(number) && row.querySelector('.gall_tit'); }); } rows = rows.slice(0, 30); async function fetchDetail(url) { const out = { detail_time: '', content: '', attached_image: '' }; try { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), 12000); const res = await fetch(url, { credentials: 'include', signal: controller.signal }); clearTimeout(timer); if (!res.ok) { out.content = 'HTTP ' + res.status; return out; } const html = await res.text(); const doc = new DOMParser().parseFromString(html, 'text/html'); if (doc.querySelector('.box_infotxt.delet')) { out.content = 'deleted or hidden post'; return out; } const dateEl = doc.querySelector('.gallview_head .gall_date'); out.detail_time = clean(dateEl?.getAttribute('title') || dateEl?.textContent); const contentEl = doc.querySelector('.writing_view_box .write_div') || doc.querySelector('.write_div') || doc.querySelector('.view_content_wrap') || doc.querySelector('.writing_view_box'); out.content = clean(contentEl?.innerText || contentEl?.textContent); const imageUrls = Array.from(doc.querySelectorAll('.appending_file_box a[href*=\"download.php\"], .write_div img, .writing_view_box img')).map(el => abs(el.getAttribute('href') || el.getAttribute('src'))).filter(u => u && !/spacer|blank|dcin_logo|fix_nik|nik\\.gif|btn|icon/i.test(u)); out.attached_image = Array.from(new Set(imageUrls)).join(' | '); } catch (e) { out.content = 'DETAIL_FETCH_ERROR: ' + (e.message || e); } return out; } for (const row of rows) { const number = clean(row.querySelector('.gall_num')?.textContent || row.getAttribute('data-no') || ''); const linkEl = row.querySelector('.gall_tit a'); let href = linkEl?.getAttribute('href') || ''; let url = ''; if (href && href !== '#' && !href.startsWith('javascript')) { url = abs(href); } else { url = 'https://gall.dcinside.com/board/view/?id=' + encodeURIComponent(galleryId) + '&no=' + encodeURIComponent(number) + '&page=' + encodeURIComponent(pageNo); } const writerBox = row.querySelector('.gall_writer'); const writerText = clean(writerBox?.textContent); const ipMatch = writerText.match(/\\(([^)]+)\\)/); const commentText = clean(row.querySelector('.reply_num, .reply_numbox')?.textContent).replace(/[\\[\\]]/g, ''); const titleRaw = clean(linkEl?.textContent || get(row, '.gall_tit')); const title = titleRaw.replace(/\\[[0-9]+\\]$/, '').trim(); const detail = await fetchDetail(url); const data = { number: number, title: title, title_url: url, comments: commentText, writer: get(row, '.gall_writer .nickname') || writerText.replace(/\\s*\\([^)]+\\)\\s*$/, ''), writer_ip: writerBox?.getAttribute('data-ip') || (ipMatch ? ipMatch[1] : ''), views: get(row, '.gall_count'), recommendations: get(row, '.gall_recommend'), detail_time: detail.detail_time, content: detail.content, attached_image: detail.attached_image }; const div = document.createElement('div'); div.className = 'uscraper-dcinside-row'; Object.entries(data).forEach(([key, value]) => { const span = document.createElement('span'); span.className = 'col-' + key; span.textContent = value || ''; div.appendChild(span); }); root.appendChild(div); await delay(150); } root.setAttribute('data-count', String(root.querySelectorAll('.uscraper-dcinside-row').length)); root.setAttribute('data-complete', '1'); })();",
        "waitForCompletion": true,
        "timeout": 240
      }
    },
    {
      "block_id": "wait-for-element-2",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 1560,
      "position_y": 220,
      "config": {
        "selector": "#uscraper-dcinside-results[data-complete=\"1\"]",
        "timeout": 240,
        "visible": false
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 1920,
      "position_y": 220,
      "config": {
        "rowSelector": "#uscraper-dcinside-results .uscraper-dcinside-row",
        "fileName": "dcinside-scraper.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "columns": [
          {
            "name": "number",
            "selector": ".col-number",
            "attribute": "text"
          },
          {
            "name": "title",
            "selector": ".col-title",
            "attribute": "text"
          },
          {
            "name": "title_url",
            "selector": ".col-title_url",
            "attribute": "text"
          },
          {
            "name": "comments",
            "selector": ".col-comments",
            "attribute": "text"
          },
          {
            "name": "writer",
            "selector": ".col-writer",
            "attribute": "text"
          },
          {
            "name": "writer_ip",
            "selector": ".col-writer_ip",
            "attribute": "text"
          },
          {
            "name": "views",
            "selector": ".col-views",
            "attribute": "text"
          },
          {
            "name": "recommendations",
            "selector": ".col-recommendations",
            "attribute": "text"
          },
          {
            "name": "detail_time",
            "selector": ".col-detail_time",
            "attribute": "text"
          },
          {
            "name": "content",
            "selector": ".col-content",
            "attribute": "text"
          },
          {
            "name": "attached_image",
            "selector": ".col-attached_image",
            "attribute": "text"
          }
        ]
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 2280,
      "position_y": 220,
      "config": {
        "duration": 1
      }
    },
    {
      "block_id": "loop-continue-1",
      "block_type": "process",
      "title": "Loop Continue",
      "description": "Continue multi-input loop",
      "position_x": 2640,
      "position_y": 220,
      "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": "wait-for-element-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-element-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": "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"
    },
    {
      "from_block_id": "structured-export-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": "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": 116,
      "width": 2480,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "wait-for-element-1",
          "wait-for-element-2",
          "sleep-1"
        ]
      }
    },
    {
      "id": "group-interaction",
      "element_type": "group",
      "title": "Interaction",
      "color": "#a56eff",
      "position_x": 1128,
      "position_y": 116,
      "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": 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": 2568,
      "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 DCInside MapleStory gallery listing pages and enriches each visible post with detail-page created time, text content, and attached image URLs. Pagination uses a safe known-URL list for pages 1-3 to avoid infinite traversal on DCInside's very large archive; extend the navigate.urls array to collect additional pages. The extraction waits for the async detail-enrichment script to finish before exporting.",
      "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 3 pages. Pair with loop-continue at the end of each iteration.",
      "color": "#ee5396",
      "position_x": 320,
      "position_y": 200,
      "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: `(async () => { const delay = ms => new Promise(r => setTimeout(r, ms)); const clean = s => (s || '')...` Verify in browser if results are empty.",
      "color": "#ee5396",
      "position_x": 1400,
      "position_y": 200,
      "width": 340,
      "height": 140,
      "z_index": 22,
      "data": {
        "block_id": "inject-javascript-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": 2840,
      "position_y": 200,
      "width": 340,
      "height": 123,
      "z_index": 22,
      "data": {
        "block_id": "loop-continue-1"
      }
    }
  ]
}