{
  "version": "1.0.0",
  "exported_at": "2026-06-03T07:30:00.000Z",
  "project": {
    "name": "Xiaohongshu Search Results Scraper by URL",
    "description": "Best-effort scraper for Xiaohongshu / Red Note search result URLs. Extracts search keyword, post title, image URL, detail page URL, author, datetime, author URL, like count, and recommended reason. Xiaohongshu search result pages are infinite-scroll listings, so this template waits for the SPA, autoscrolls to load available cards, normalizes detected note cards/links into export rows, and writes them to CSV. If Xiaohongshu redirects to a security, login, CAPTCHA, or invalid-token page, the template writes a diagnostic row indicating that manual login/CAPTCHA solving in the built-in browser profile is required.",
    "color": "bg-[#ff2442]",
    "template_id": "ai-generated"
  },
  "blocks": [
    {
      "block_id": "navigate-1",
      "block_type": "process",
      "title": "Navigate",
      "description": "Go to a URL",
      "position_x": 120,
      "position_y": 260,
      "config": {
        "url": "https://www.xiaohongshu.com/search_result?keyword=web%20scraping&source=web_search_result_notes&type=51",
        "color": "bg-[#ff2442]"
      }
    },
    {
      "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": 260,
      "config": {
        "timeout": 45
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for Xiaohongshu SPA rendering, login redirects, or anti-bot pages",
      "position_x": 840,
      "position_y": 260,
      "config": {
        "duration": 8
      }
    },
    {
      "block_id": "inject-javascript-1",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Autoscroll and normalize Xiaohongshu result cards into export rows",
      "position_x": 1200,
      "position_y": 260,
      "config": {
        "jsCode": "(async () => { const sleep = ms => new Promise(r => setTimeout(r, ms)); const clean = v => String(v || '').replace(/\\s+/g, ' ').trim(); const abs = href => { try { return href ? new URL(href, location.origin).href : ''; } catch(e) { return href || ''; } }; const rawKeyword = new URL(location.href).searchParams.get('keyword') || ''; let keyword = rawKeyword; try { keyword = decodeURIComponent(rawKeyword); } catch(e) {} for (const txt of ['同意','接受','Accept','关闭','Close','跳过','稍后再说']) { for (const el of Array.from(document.querySelectorAll('button, .btn, [role=\"button\"], a'))) { if (clean(el.textContent).includes(txt)) { try { el.click(); await sleep(400); } catch(e) {} } } } const maxScrolls = 50; const idleRoundsNeeded = 4; let idleRounds = 0; let lastHeight = document.body.scrollHeight || 0; for (let i = 0; i < maxScrolls && idleRounds < idleRoundsNeeded; i++) { window.scrollTo(0, document.body.scrollHeight); await sleep(1800); const h = document.body.scrollHeight || 0; const count = document.querySelectorAll('section.note-item, div.note-item, .feeds-container section, .feeds-page section, a[href*=\"/search_result/\"], a[href*=\"/explore/\"], a[href*=\"/discovery/item/\"]').length; if (h <= lastHeight + 50) idleRounds++; else { idleRounds = 0; lastHeight = h; } if (count > 0 && i > 8 && idleRounds >= 2) break; } window.scrollTo(0, 0); await sleep(800); let root = document.getElementById('uscraper-xhs-output'); if (root) root.remove(); root = document.createElement('div'); root.id = 'uscraper-xhs-output'; root.style.cssText = 'position:absolute;left:-99999px;top:0;width:1px;height:1px;overflow:hidden;'; document.body.appendChild(root); const cardSelectors = 'section.note-item, div.note-item, .feeds-container section, .feeds-page section, [class*=\"note-item\"], [class*=\"NoteItem\"], [class*=\"noteCard\"], [class*=\"NoteCard\"], a[href*=\"/search_result/\"], a[href*=\"/explore/\"], a[href*=\"/discovery/item/\"]'; const candidates = Array.from(document.querySelectorAll(cardSelectors)); const seen = new Set(); const rows = []; const getCard = el => el.matches && el.matches('a[href]') ? (el.closest('section.note-item, div.note-item, .note-item, section, article, [class*=\"card\"], [class*=\"Card\"]') || el.parentElement || el) : el; for (const el of candidates) { const card = getCard(el); if (!card || card.id === 'uscraper-xhs-output' || root.contains(card)) continue; const links = (el.matches && el.matches('a[href]') ? [el] : []).concat(Array.from(card.querySelectorAll ? card.querySelectorAll('a[href]') : [])); const detailLink = links.find(a => { const h = a.getAttribute('href') || ''; return !h.includes('/user/profile/') && (h.includes('/search_result/') || h.includes('/explore/') || h.includes('/discovery/item/')); }) || links.find(a => !((a.getAttribute('href') || '').includes('/user/profile/'))); const authorLink = links.find(a => (a.getAttribute('href') || '').includes('/user/profile/')); const img = card.querySelector ? card.querySelector('img') : null; const titleEl = card.querySelector ? card.querySelector('.title span, .title, .note-title, [class*=\"title\"], a[title], span') : null; const authorEl = card.querySelector ? card.querySelector('.author .name, .author-wrapper .name, a[href*=\"/user/profile\"] span, .user-name, .nickname, [class*=\"author\"] [class*=\"name\"], [class*=\"user\"] [class*=\"name\"]') : null; const timeEl = card.querySelector ? card.querySelector('time, .date, .time, .publish-time, [class*=\"date\"], [class*=\"time\"]') : null; const reasonEl = card.querySelector ? card.querySelector('.recommend-reason, .reason, [class*=\"recommend\"], [class*=\"reason\"]') : null; const title = clean((titleEl && (titleEl.getAttribute('title') || titleEl.textContent)) || (img && img.getAttribute('alt')) || (detailLink && detailLink.textContent) || ''); const imageUrl = img ? (img.currentSrc || img.src || img.getAttribute('data-src') || img.getAttribute('src') || '') : ''; const detailUrl = detailLink ? abs(detailLink.getAttribute('href') || detailLink.href) : ''; const author = clean((authorEl && authorEl.textContent) || (authorLink && authorLink.textContent) || ''); const authorUrl = authorLink ? abs(authorLink.getAttribute('href') || authorLink.href) : ''; const text = clean(card.textContent || ''); const datetimeMatch = clean(timeEl && timeEl.textContent).match(/(\\d{4}[.-]\\d{1,2}[.-]\\d{1,2}|\\d{4}-\\d{1,2}-\\d{1,2}|\\d{1,2}-\\d{1,2}|\\d+天前|\\d+小时前|\\d+分钟前|昨天|前天|刚刚)/) || text.match(/(\\d{4}[.-]\\d{1,2}[.-]\\d{1,2}|\\d{4}-\\d{1,2}-\\d{1,2}|\\d{1,2}-\\d{1,2}|\\d+天前|\\d+小时前|\\d+分钟前|昨天|前天|刚刚)/); const likeCandidates = card.querySelectorAll ? Array.from(card.querySelectorAll('.like-wrapper .count, .like .count, [class*=\"like\"] [class*=\"count\"], [class*=\"like\"], .count, span')) : []; const likeEl = likeCandidates.find(x => /[\\d,.万千kK]+/.test(clean(x.textContent)) && !/(评论|收藏|分享)/.test(clean(x.textContent))); const likeText = clean(likeEl && likeEl.textContent); const likeMatch = likeText.match(/[\\d,.万千kK]+/); const row = { input_keywords: keyword, title, image_url: imageUrl, details_page_url: detailUrl, author, datetime: datetimeMatch ? datetimeMatch[0] : '', author_url: authorUrl, like_count: likeMatch ? likeMatch[0] : likeText, recommended_reason: clean(reasonEl && reasonEl.textContent) }; const key = row.details_page_url || row.image_url || row.title; if (!key || seen.has(key)) continue; if (!row.details_page_url && !row.image_url && !row.title) continue; seen.add(key); rows.push(row); } if (rows.length === 0) { const bodyText = clean(document.body ? document.body.innerText : ''); const blocked = /(安全限制|验证码|登录|login|captcha|invalid|url is invalid|错误|error|verify|verification)/i.test(bodyText + ' ' + document.title); rows.push({ input_keywords: keyword, title: blocked ? 'Xiaohongshu access blocked or verification required' : 'No Xiaohongshu result cards detected', image_url: '', details_page_url: location.href, author: '', datetime: '', author_url: '', like_count: '', recommended_reason: clean((document.title || '') + ' ' + bodyText.slice(0, 240)) }); } for (const r of rows) { const div = document.createElement('div'); div.className = 'uscraper-xhs-row'; div.setAttribute('data-input-keywords', r.input_keywords || ''); div.setAttribute('data-title', r.title || ''); div.setAttribute('data-image-url', r.image_url || ''); div.setAttribute('data-details-page-url', r.details_page_url || ''); div.setAttribute('data-author', r.author || ''); div.setAttribute('data-datetime', r.datetime || ''); div.setAttribute('data-author-url', r.author_url || ''); div.setAttribute('data-like-count', r.like_count || ''); div.setAttribute('data-recommended-reason', r.recommended_reason || ''); root.appendChild(div); } return rows.length; })()",
        "waitForCompletion": true,
        "timeout": 160
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait for normalized UScraper export rows",
      "position_x": 1560,
      "position_y": 260,
      "config": {
        "selector": ".uscraper-xhs-row",
        "timeout": 20,
        "visible": false
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export normalized Xiaohongshu search result rows",
      "position_x": 1920,
      "position_y": 260,
      "config": {
        "rowSelector": ".uscraper-xhs-row",
        "fileName": "xiaohongshu-search-results-scraper.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "columns": [
          {
            "name": "input_keywords",
            "selector": "",
            "attribute": "data-input-keywords"
          },
          {
            "name": "title",
            "selector": "",
            "attribute": "data-title"
          },
          {
            "name": "image_url",
            "selector": "",
            "attribute": "data-image-url"
          },
          {
            "name": "details_page_url",
            "selector": "",
            "attribute": "data-details-page-url"
          },
          {
            "name": "author",
            "selector": "",
            "attribute": "data-author"
          },
          {
            "name": "datetime",
            "selector": "",
            "attribute": "data-datetime"
          },
          {
            "name": "author_url",
            "selector": "",
            "attribute": "data-author-url"
          },
          {
            "name": "like_count",
            "selector": "",
            "attribute": "data-like-count"
          },
          {
            "name": "recommended_reason",
            "selector": "",
            "attribute": "data-recommended-reason"
          }
        ]
      }
    },
    {
      "block_id": "end-1",
      "block_type": "output",
      "title": "End",
      "description": "Terminate execution flow after export",
      "position_x": 2280,
      "position_y": 260,
      "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": "sleep-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-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-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": "end-1",
      "to_connector_id": "left"
    }
  ],
  "canvas_elements": [
    {
      "id": "group-load",
      "element_type": "group",
      "title": "Page Load",
      "color": "#08bdba",
      "position_x": 48,
      "position_y": 156,
      "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": 1128,
      "position_y": 156,
      "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": 156,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "structured-export-1"
        ]
      }
    },
    {
      "id": "group-control",
      "element_type": "group",
      "title": "Control Flow",
      "color": "#8d8d8d",
      "position_x": 2208,
      "position_y": 156,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "end-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Best-effort scraper for Xiaohongshu / Red Note search result URLs. Extracts search keyword, post title, image URL, detail page URL, author, datetime, author URL, like count, and recommended reason. Xiaohongshu search result pages are infinite-scroll listings, so this template waits for the SPA, autoscrolls to load available cards, normalizes detected note cards/links into export rows, and writes them to CSV. If Xiaohongshu redirects to a security, login, CAPTCHA, or invalid-token page, the template writes a diagnostic row indicating that manual login/CAPTCHA solving in the built-in browser profile is required.",
      "color": "#f1c21b",
      "position_x": 80,
      "position_y": 20,
      "width": 480,
      "height": 160,
      "z_index": 22,
      "data": {}
    },
    {
      "id": "note-block-inject-javascript-1",
      "element_type": "note",
      "title": "Note: Inject JavaScript",
      "content": "Runs custom JavaScript in the page: `(async () => { const sleep = ms => new Promise(r => setTimeout(r, ms)); const clean = v => String(v ...` Verify in browser if results are empty.",
      "color": "#ee5396",
      "position_x": 1400,
      "position_y": 240,
      "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": "Extracts rows matching `.uscraper-xhs-row`. Confirm row count > 0 before running at scale.",
      "color": "#ee5396",
      "position_x": 2120,
      "position_y": 240,
      "width": 340,
      "height": 110,
      "z_index": 22,
      "data": {
        "block_id": "structured-export-1"
      }
    }
  ]
}