{
  "version": "1.0.0",
  "exported_at": "2026-05-31T14:45:00.000Z",
  "project": {
    "name": "eBay Scraper Store Listing",
    "description": "Best-effort UScraper equivalent for Octoparse's eBay Scraper (Store Listing). It loops through configured eBay seller inventory/RSS page URLs for the sample stores, attempts to normalize RSS <item> entries, visible /itm/ links, and embedded eBay item URLs into stable rows, then appends results into ebay-scraper-store-listing.csv. If eBay returns an empty/bot-protected/CAPTCHA page, the template writes a diagnostic row for that seller/page instead of silently producing no CSV. Store followers, feedback score, sales, and store image are extracted only when exposed by the page/feed.",
    "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://www.ebay.com/sch/i.html?_ssn=864autoparts&_ipg=200&_pgn=1&_rss=1",
          "https://www.ebay.com/sch/i.html?_ssn=864autoparts&_ipg=200&_pgn=2&_rss=1",
          "https://www.ebay.com/sch/i.html?_ssn=864autoparts&_ipg=200&_pgn=3&_rss=1",
          "https://www.ebay.com/sch/i.html?_ssn=worldofbooksusa&_ipg=200&_pgn=1&_rss=1",
          "https://www.ebay.com/sch/i.html?_ssn=worldofbooksusa&_ipg=200&_pgn=2&_rss=1",
          "https://www.ebay.com/sch/i.html?_ssn=worldofbooksusa&_ipg=200&_pgn=3&_rss=1",
          "https://www.ebay.com/sch/i.html?_ssn=haleehd&_ipg=200&_pgn=1&_rss=1",
          "https://www.ebay.com/sch/i.html?_ssn=haleehd&_ipg=200&_pgn=2&_rss=1",
          "https://www.ebay.com/sch/i.html?_ssn=haleehd&_ipg=200&_pgn=3&_rss=1"
        ],
        "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": 45
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 840,
      "position_y": 220,
      "config": {
        "duration": 2
      }
    },
    {
      "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": "(() => {\n  document.querySelectorAll('[data-uscraper-ebay-row],[data-uscraper-ebay-container]').forEach(e => e.remove());\n  const params = new URL(location.href).searchParams;\n  const seller = params.get('_ssn') || (location.pathname.match(/\\/sch\\/([^\\/]+)/i) || [,''])[1] || '';\n  const page = params.get('_pgn') || params.get('page') || '1';\n  const clean = v => String(v || '').replace(/\\s+/g, ' ').trim();\n  const htmlDecode = s => { const t = document.createElement('textarea'); t.innerHTML = String(s || ''); return t.value; };\n  const stripTags = s => clean(String(s || '').replace(/<[^>]+>/g, ' '));\n  const getTag = (node, tag) => { const el = node.querySelector(tag); return el ? clean(el.textContent || '') : ''; };\n  const source = (document.documentElement && (document.documentElement.outerHTML || document.documentElement.innerText || document.documentElement.textContent)) || '';\n  const pageText = clean((document.body && document.body.innerText) || document.documentElement.textContent || '');\n  const followers = (pageText.match(/[\\d,.]+\\s*[KM]?\\s+followers/i) || [''])[0];\n  const feedback = ((pageText.match(/(\\d{1,3}(?:\\.\\d+)?%)\\s*positive\\s+feedback/i) || pageText.match(/feedback[^\\n]{0,80}?(\\d{1,3}(?:\\.\\d+)?%)/i) || [,''])[1] || '');\n  const sales = (pageText.match(/[\\d,.]+\\s*[KM]?\\s+items\\s+sold/i) || pageText.match(/[\\d,.]+\\s*[KM]?\\s+sold/i) || [''])[0];\n  const storeImgEl = Array.from(document.querySelectorAll('img')).find(i => /ebayimg\\.com\\/images\\/g\\//i.test(i.currentSrc || i.src || ''));\n  const storeImg = storeImgEl ? (storeImgEl.currentSrc || storeImgEl.src || '') : '';\n  const baseStoreUrl = seller ? ('https://www.ebay.com/str/' + seller) : location.href;\n  const rows = [];\n  const pushRow = r => {\n    if (!r.title_url && !r.title) return;\n    rows.push({\n      store_name: seller,\n      followers,\n      feedback_score: feedback,\n      sales,\n      store_img_url: storeImg,\n      page_url: baseStoreUrl,\n      title: clean(r.title),\n      title_url: clean(r.title_url),\n      price: clean(r.price),\n      condition: clean(r.condition),\n      image_url: clean(r.image_url),\n      discount: clean(r.discount),\n      shipping: clean(r.shipping),\n      page\n    });\n  };\n  let items = Array.from(document.querySelectorAll('item'));\n  if (!items.length) {\n    try {\n      const parsed = new DOMParser().parseFromString(source, 'text/xml');\n      items = Array.from(parsed.querySelectorAll('item'));\n    } catch (e) {}\n  }\n  for (const item of items) {\n    const title = getTag(item, 'title');\n    const link = getTag(item, 'link');\n    const descRaw = getTag(item, 'description');\n    const desc = htmlDecode(descRaw);\n    const descText = stripTags(desc);\n    const imgMatch = desc.match(/<img[^>]+src=[\"']([^\"']+)[\"']/i) || desc.match(/https?:\\/\\/i\\.ebayimg\\.com\\/[^\\s\"'<>]+/i);\n    const priceMatch = descText.match(/(?:US\\s*)?\\$\\s?[\\d,.]+(?:\\s*to\\s*(?:US\\s*)?\\$\\s?[\\d,.]+)?/i);\n    const conditionMatch = descText.match(/Condition\\s*:\\s*([^|;\\n\\r]+)/i) || descText.match(/\\b(New|Used|Pre-Owned|Open Box|Certified Refurbished|Seller Refurbished|For parts or not working)\\b/i);\n    const shippingMatch = descText.match(/Shipping\\s*:\\s*([^|;\\n\\r]+)/i) || descText.match(/(?:Free shipping|\\+?\\$[\\d,.]+\\s+shipping)/i);\n    const discountMatch = descText.match(/(?:\\d+%\\s*off|save\\s*\\$?[\\d,.]+|was\\s*\\$?[\\d,.]+)/i);\n    pushRow({ title, title_url: link, price: priceMatch ? priceMatch[0] : '', condition: conditionMatch ? (conditionMatch[1] || conditionMatch[0]) : '', image_url: imgMatch ? (imgMatch[1] || imgMatch[0]) : '', discount: discountMatch ? discountMatch[0] : '', shipping: shippingMatch ? (shippingMatch[1] || shippingMatch[0]) : '' });\n  }\n  const seen = new Set(rows.map(r => r.title_url));\n  const anchors = Array.from(document.querySelectorAll('a[href*=\"/itm/\"], a[href*=\"itm/\"]'));\n  for (const a of anchors) {\n    const href = a.href || a.getAttribute('href') || '';\n    const url = href.split('?')[0];\n    if (!url || seen.has(url)) continue;\n    seen.add(url);\n    let card = a.closest('li, article, div') || a.parentElement || a;\n    for (let p = a, i = 0; p && i < 8; p = p.parentElement, i++) {\n      const tx = clean(p.innerText || p.textContent || '');\n      if ((/\\$\\s?[\\d,.]+|Free shipping|Pre-Owned|New|Used/i.test(tx)) && tx.length > 20) { card = p; break; }\n    }\n    const cardText = clean(card.innerText || card.textContent || '');\n    const img = card.querySelector('img[src], img[data-src]');\n    const title = clean(a.innerText || a.getAttribute('title') || a.getAttribute('aria-label') || card.querySelector('h3,h2,.s-item__title')?.textContent || '');\n    const priceMatch = cardText.match(/\\$\\s?[\\d,.]+(?:\\s*to\\s*\\$\\s?[\\d,.]+)?/);\n    const conditionMatch = cardText.match(/\\b(New|Used|Pre-Owned|Open Box|Certified Refurbished|Seller Refurbished|For parts or not working)\\b/i);\n    const shippingMatch = cardText.match(/(?:Free shipping|\\+?\\$[\\d,.]+\\s+shipping)/i);\n    if (title && url && !/shop on ebay/i.test(title)) pushRow({ title, title_url: url, price: priceMatch ? priceMatch[0] : '', condition: conditionMatch ? conditionMatch[0] : '', image_url: img ? (img.currentSrc || img.src || img.getAttribute('data-src') || '') : '', discount: '', shipping: shippingMatch ? shippingMatch[0] : '' });\n  }\n  const urlMatches = Array.from(source.matchAll(/https?:\\\\?\\/\\\\?\\/www\\.ebay\\.com\\\\?\\/itm\\\\?\\/[^\"'<>\\\\\\s]+/gi)).map(m => m[0].replace(/\\\\\\//g, '/'));\n  for (const rawUrl of urlMatches) {\n    const url = rawUrl.split('?')[0];\n    if (!url || seen.has(url)) continue;\n    seen.add(url);\n    pushRow({ title: 'Embedded eBay item URL', title_url: url, price: '', condition: '', image_url: '', discount: '', shipping: '' });\n  }\n  if (!rows.length) {\n    const blocked = /captcha|pardon|robot|access denied|verify|unusual traffic|blocked/i.test(pageText + ' ' + source);\n    pushRow({\n      title: blocked ? 'EBAY_BLOCKED_OR_CAPTCHA_NO_ITEMS_EXTRACTED' : 'NO_ITEMS_FOUND_ON_EBAY_PAGE',\n      title_url: location.href,\n      price: '',\n      condition: blocked ? 'Blocked/CAPTCHA or bot-protected response' : 'No visible item records in automated session',\n      image_url: '',\n      discount: '',\n      shipping: ''\n    });\n  }\n  const container = document.createElement('div');\n  container.setAttribute('data-uscraper-ebay-container', '1');\n  container.setAttribute('style', 'display:none');\n  for (const r of rows) {\n    const d = document.createElement('div');\n    d.setAttribute('data-uscraper-ebay-row', '1');\n    d.setAttribute('data-store-name', r.store_name || '');\n    d.setAttribute('data-followers', r.followers || '');\n    d.setAttribute('data-feedback-score', r.feedback_score || '');\n    d.setAttribute('data-sales', r.sales || '');\n    d.setAttribute('data-store-img-url', r.store_img_url || '');\n    d.setAttribute('data-page-url', r.page_url || '');\n    d.setAttribute('data-title', r.title || '');\n    d.setAttribute('data-title-url', r.title_url || '');\n    d.setAttribute('data-price', r.price || '');\n    d.setAttribute('data-condition', r.condition || '');\n    d.setAttribute('data-image-url', r.image_url || '');\n    d.setAttribute('data-discount', r.discount || '');\n    d.setAttribute('data-shipping', r.shipping || '');\n    d.setAttribute('data-page', r.page || page);\n    container.appendChild(d);\n  }\n  (document.body || document.documentElement).appendChild(container);\n  return 'normalized ebay rows: ' + rows.length;\n})()",
        "waitForCompletion": true,
        "timeout": 20
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 1560,
      "position_y": 220,
      "config": {
        "rowSelector": "[data-uscraper-ebay-row]",
        "fileName": "ebay-scraper-store-listing.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "columns": [
          {
            "name": "store_name",
            "selector": "",
            "attribute": "data-store-name"
          },
          {
            "name": "followers",
            "selector": "",
            "attribute": "data-followers"
          },
          {
            "name": "feedback_score",
            "selector": "",
            "attribute": "data-feedback-score"
          },
          {
            "name": "sales",
            "selector": "",
            "attribute": "data-sales"
          },
          {
            "name": "store_img_url",
            "selector": "",
            "attribute": "data-store-img-url"
          },
          {
            "name": "page_url",
            "selector": "",
            "attribute": "data-page-url"
          },
          {
            "name": "title",
            "selector": "",
            "attribute": "data-title"
          },
          {
            "name": "title_url",
            "selector": "",
            "attribute": "data-title-url"
          },
          {
            "name": "price",
            "selector": "",
            "attribute": "data-price"
          },
          {
            "name": "condition",
            "selector": "",
            "attribute": "data-condition"
          },
          {
            "name": "image_url",
            "selector": "",
            "attribute": "data-image-url"
          },
          {
            "name": "discount",
            "selector": "",
            "attribute": "data-discount"
          },
          {
            "name": "shipping",
            "selector": "",
            "attribute": "data-shipping"
          },
          {
            "name": "page",
            "selector": "",
            "attribute": "data-page"
          }
        ]
      }
    },
    {
      "block_id": "loop-continue-1",
      "block_type": "process",
      "title": "Loop Continue",
      "description": "Continue multi-input loop",
      "position_x": 1920,
      "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": "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": "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": 116,
      "width": 1040,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "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": 1488,
      "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": 1848,
      "position_y": 116,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "loop-continue-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Best-effort UScraper equivalent for Octoparse's eBay Scraper (Store Listing). It loops through configured eBay seller inventory/RSS page URLs for the sample stores, attempts to normalize RSS <item> entries, visible /itm/ links, and embedded eBay item URLs into stable rows, then appends results into ebay-scraper-store-listing.csv. If eBay returns an empty/bot-protected/CAPTCHA page, the template writes a diagnostic row for that seller/page instead of silently producing no CSV. Store followers, feedback score, sales, and store image are extracted only when exposed by the page/feed.",
      "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 9 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: `(() => {\n  document.querySelectorAll('[data-uscraper-ebay-row],[data-uscraper-ebay-container]').forE...` 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-structured-export-1",
      "element_type": "note",
      "title": "Note: Structured Export",
      "content": "Extracts rows matching `[data-uscraper-ebay-row]`. Confirm row count > 0 before running at scale.",
      "color": "#ee5396",
      "position_x": 1760,
      "position_y": 200,
      "width": 340,
      "height": 112,
      "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": 2120,
      "position_y": 200,
      "width": 340,
      "height": 123,
      "z_index": 22,
      "data": {
        "block_id": "loop-continue-1"
      }
    }
  ]
}