{
  "version": "1.0.0",
  "exported_at": "2026-05-31T19:15:00.000Z",
  "project": {
    "name": "Carsensor Car Listings Scraper",
    "description": "Extracts used vehicle listing data from CarSensor, including vehicle name, vehicle information, dealer, model year, mileage, repair history, inspection status, statutory maintenance, warranty, prices/plans, image URL, and listing URL. The supplied Octoparse sample detail/image URLs were expired/404, so this template starts from a live CarSensor Mercedes-Benz C-Class listing URL inferred from the sample data. Pagination is handled by clicking the real non-detail Next pagination link, including labels such as 次 or 次の30件, until no enabled Next link remains; all pages are appended into one CSV.",
    "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": {
        "url": "https://www.carsensor.net/usedcar/bME/s004/index.html",
        "color": "bg-[#4589ff]",
        "tags": [
          "carsensor",
          "used-cars",
          "listing"
        ]
      }
    },
    {
      "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,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "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": "//div[contains(concat(' ', normalize-space(@class), ' '), ' cassetteWrap ')][.//a[contains(@href,'/usedcar/detail/')]][contains(normalize-space(.),'年式') and contains(normalize-space(.),'走行距離')]",
        "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": 1200,
      "position_y": 220,
      "config": {
        "rowSelector": "//div[contains(concat(' ', normalize-space(@class), ' '), ' cassetteWrap ')][.//a[contains(@href,'/usedcar/detail/')]][contains(normalize-space(.),'年式') and contains(normalize-space(.),'走行距離')]",
        "fileName": "carsensor-used-car-listings-scraper.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "color": "bg-[#42be65]",
        "columns": [
          {
            "name": "車名",
            "selector": "(()=>{const norm=s=>(s||'').replace(/&nbsp;/g,' ').replace(/\\s+/g,' ').trim(); const html=ROW.innerHTML||''; const unesc=s=>norm((s||'').replace(/&nbsp;/g,' ').replace(/&amp;/g,'&').replace(/&quot;/g,'\"').replace(/&#39;/g,\"'\")); const img=ROW.querySelector('img[alt],img[title]'); let raw=(img&&(img.getAttribute('alt')||img.getAttribute('title')))||''; if(!raw){const m=html.match(/(?:alt|title)=[\"']([^\"']{3,500})[\"']/i); raw=m?m[1]:'';} raw=unesc(raw); const link=Array.from(ROW.querySelectorAll('a[href*=\"/usedcar/detail/\"]')).map(a=>norm(a.textContent)).find(t=>t&&!t.includes('document.write')&&!t.includes('<img')&&t.length<80); if((!raw||raw.includes('animation_'))&&link) raw=link; const parts=raw.split(' ').filter(Boolean); return parts.length>=2?parts.slice(0,2).join(' '):raw;})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "車両情報",
            "selector": "(()=>{const norm=s=>(s||'').replace(/&nbsp;/g,' ').replace(/\\s+/g,' ').trim(); const html=ROW.innerHTML||''; const unesc=s=>norm((s||'').replace(/&nbsp;/g,' ').replace(/&amp;/g,'&').replace(/&quot;/g,'\"').replace(/&#39;/g,\"'\")); const img=ROW.querySelector('img[alt],img[title]'); let raw=(img&&(img.getAttribute('alt')||img.getAttribute('title')))||''; if(!raw){const m=html.match(/(?:alt|title)=[\"']([^\"']{3,1000})[\"']/i); raw=m?m[1]:'';} if(raw&&!raw.includes('animation_')) return unesc(raw); const link=Array.from(ROW.querySelectorAll('a[href*=\"/usedcar/detail/\"]')).map(a=>norm(a.textContent)).find(t=>t&&!t.includes('document.write')&&!t.includes('<img')&&t.length<200); return link||'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "販売店名",
            "selector": "(()=>{const norm=s=>(s||'').replace(/\\s+/g,' ').trim(); const els=Array.from(ROW.querySelectorAll('.cassetteMain__shop, .cassetteSub__shop, [class*=\"shop\"] a, [class*=\"shop\"]')); const val=els.map(e=>norm(e.textContent)).find(t=>t&&t.length>1); return val||'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "年式",
            "selector": "(()=>{const norm=s=>(s||'').replace(/\\s+/g,' ').trim(); const keys=Array.from(ROW.querySelectorAll('dt,th')); for(const k of keys){if(norm(k.textContent).includes('年式')){const sib=k.nextElementSibling; if(sib) return norm(sib.textContent); const tr=k.closest('tr'); const td=tr&&tr.querySelector('td'); if(td) return norm(td.textContent);}} const txt=norm(ROW.textContent); const m=txt.match(/年式\\s*([^\\s]+(?:\\([^\\)]*\\))?)/); return m?m[1]:'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "走行距離",
            "selector": "(()=>{const norm=s=>(s||'').replace(/\\s+/g,' ').trim(); const keys=Array.from(ROW.querySelectorAll('dt,th')); for(const k of keys){if(norm(k.textContent).includes('走行距離')){const sib=k.nextElementSibling; if(sib) return norm(sib.textContent); const tr=k.closest('tr'); const td=tr&&tr.querySelector('td'); if(td) return norm(td.textContent);}} const txt=norm(ROW.textContent); const m=txt.match(/走行距離\\s*([^\\s]+)/); return m?m[1]:'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "修復歴",
            "selector": "(()=>{const norm=s=>(s||'').replace(/\\s+/g,' ').trim(); const keys=Array.from(ROW.querySelectorAll('dt,th')); for(const k of keys){if(norm(k.textContent).includes('修復歴')){const sib=k.nextElementSibling; if(sib) return norm(sib.textContent); const tr=k.closest('tr'); const td=tr&&tr.querySelector('td'); if(td) return norm(td.textContent);}} const txt=norm(ROW.textContent); const m=txt.match(/修復歴\\s*([^\\s]+)/); return m?m[1]:'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "車検",
            "selector": "(()=>{const norm=s=>(s||'').replace(/\\s+/g,' ').trim(); const keys=Array.from(ROW.querySelectorAll('dt,th')); for(const k of keys){if(norm(k.textContent).includes('車検')){const sib=k.nextElementSibling; if(sib) return norm(sib.textContent); const tr=k.closest('tr'); const td=tr&&tr.querySelector('td'); if(td) return norm(td.textContent);}} const txt=norm(ROW.textContent); const m=txt.match(/車検\\s*([^\\s]+)/); return m?m[1]:'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "法定整備",
            "selector": "(()=>{const norm=s=>(s||'').replace(/\\s+/g,' ').trim(); const trim=s=>norm(s).split(/保証|排気量|ミッション|後で|お気に入り|無料在庫確認/)[0].trim(); const keys=Array.from(ROW.querySelectorAll('dt,th')); for(const k of keys){if(norm(k.textContent).includes('法定整備')){const sib=k.nextElementSibling; if(sib) return trim(sib.textContent); const tr=k.closest('tr'); const td=tr&&tr.querySelector('td'); if(td) return trim(td.textContent);}} const txt=norm(ROW.textContent); const m=txt.match(/法定整備\\s*([^保証]+)/); return m?trim(m[1]):'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "保証",
            "selector": "(()=>{const norm=s=>(s||'').replace(/\\s+/g,' ').trim(); const trim=s=>norm(s).split(/車両本体価格|本体価格|支払総額|基本プラン|Aプラン|Bプラン|無料在庫確認/)[0].trim(); const keys=Array.from(ROW.querySelectorAll('dt,th')); for(const k of keys){if(norm(k.textContent).includes('保証')){const sib=k.nextElementSibling; if(sib) return trim(sib.textContent); const tr=k.closest('tr'); const td=tr&&tr.querySelector('td'); if(td) return trim(td.textContent);}} const txt=norm(ROW.textContent); const m=txt.match(/保証\\s*([^車両本体価格]+)/); return m?trim(m[1]):'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "車両本体価格",
            "selector": "(()=>{const norm=s=>(s||'').replace(/\\s+/g,' ').trim(); const keys=Array.from(ROW.querySelectorAll('dt,th')); for(const k of keys){const kt=norm(k.textContent); if(kt.includes('車両本体価格')||kt.includes('本体価格')){const sib=k.nextElementSibling; if(sib) return norm(sib.textContent); const tr=k.closest('tr'); const td=tr&&tr.querySelector('td'); if(td) return norm(td.textContent);}} const price=ROW.querySelector('.basePrice__price, .totalPrice__base, .recommendCar__price span, [class*=\"price\"] span'); return price?norm(price.textContent).replace(/万円?$/,'')+'万円':'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "基本プラン",
            "selector": "(()=>{const norm=s=>(s||'').replace(/\\s+/g,' ').trim(); const keys=Array.from(ROW.querySelectorAll('dt,th')); for(const k of keys){const kt=norm(k.textContent); if(kt.includes('基本プラン')||kt==='基本'){const sib=k.nextElementSibling; if(sib) return norm(sib.textContent); const tr=k.closest('tr'); const td=tr&&tr.querySelector('td'); if(td) return norm(td.textContent);}} return '';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Aプラン",
            "selector": "(()=>{const norm=s=>(s||'').replace(/\\s+/g,' ').trim(); const keys=Array.from(ROW.querySelectorAll('dt,th')); for(const k of keys){const kt=norm(k.textContent); if(kt.includes('Aプラン')||kt.includes('Ａプラン')){const sib=k.nextElementSibling; if(sib) return norm(sib.textContent); const tr=k.closest('tr'); const td=tr&&tr.querySelector('td'); if(td) return norm(td.textContent);}} return '';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Bプラン",
            "selector": "(()=>{const norm=s=>(s||'').replace(/\\s+/g,' ').trim(); const keys=Array.from(ROW.querySelectorAll('dt,th')); for(const k of keys){const kt=norm(k.textContent); if(kt.includes('Bプラン')||kt.includes('Ｂプラン')){const sib=k.nextElementSibling; if(sib) return norm(sib.textContent); const tr=k.closest('tr'); const td=tr&&tr.querySelector('td'); if(td) return norm(td.textContent);}} return '';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "画像のURL",
            "selector": "(()=>{const html=ROW.innerHTML||''; const abs=s=>{try{return new URL(s,location.href).href;}catch(e){return s||'';}}; const m=html.match(/data-original=[\"']([^\"']+)[\"']/i)||html.match(/data-src=[\"']([^\"']+)[\"']/i); if(m&&m[1]&&!m[1].includes('animation_')) return abs(m[1]); const imgs=Array.from(ROW.querySelectorAll('img[src], img[data-src], img[data-original]')); for(const img of imgs){const src=img.getAttribute('data-original')||img.getAttribute('data-src')||img.getAttribute('src')||''; if(src&&!src.includes('animation_')) return abs(src);} return '';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "ページのURL",
            "selector": "(()=>{const a=Array.from(ROW.querySelectorAll('a[href*=\"/usedcar/detail/\"]')).find(x=>!String(x.getAttribute('href')||'').includes('javascript:')); if(!a) return ''; try{return new URL(a.getAttribute('href'), location.href).href;}catch(e){return a.getAttribute('href')||'';}})()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "element-exists-1",
      "block_type": "process",
      "title": "Element Exists",
      "description": "Check if element exists",
      "position_x": 1560,
      "position_y": 220,
      "config": {
        "selector": "(//a[not(contains(@href,'/usedcar/detail/')) and not(contains(@onclick,'/usedcar/detail/')) and not(contains(@href,'javascript:')) and (contains(normalize-space(.),'次') or @rel='next' or contains(concat(' ', normalize-space(@class), ' '), ' next ')) and not(contains(@class,'disabled')) and not(contains(@class,'is-disabled')) and not(ancestor::*[contains(@class,'disabled') or contains(@class,'is-disabled')])])[last()]",
        "color": "bg-[#ff832b]"
      }
    },
    {
      "block_id": "end-1",
      "block_type": "output",
      "title": "End",
      "description": "Terminate execution flow",
      "position_x": 1560,
      "position_y": 560,
      "config": {
        "color": "bg-[#8d8d8d]"
      }
    },
    {
      "block_id": "click-1",
      "block_type": "process",
      "title": "Click",
      "description": "Click on element",
      "position_x": 1920,
      "position_y": 560,
      "config": {
        "selector": "(//a[not(contains(@href,'/usedcar/detail/')) and not(contains(@onclick,'/usedcar/detail/')) and not(contains(@href,'javascript:')) and (contains(normalize-space(.),'次') or @rel='next' or contains(concat(' ', normalize-space(@class), ' '), ' next ')) and not(contains(@class,'disabled')) and not(contains(@class,'is-disabled')) and not(ancestor::*[contains(@class,'disabled') or contains(@class,'is-disabled')])])[last()]",
        "timeout": 15,
        "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": 2280,
      "position_y": 560,
      "config": {
        "timeout": 30,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 2640,
      "position_y": 560,
      "config": {
        "duration": 2,
        "color": "bg-[#ff832b]"
      }
    }
  ],
  "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": "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-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": 2840,
      "height": 636,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "wait-for-element-1",
          "wait-for-page-load-2",
          "sleep-1"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 1128,
      "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": 1488,
      "position_y": 116,
      "width": 680,
      "height": 636,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "element-exists-1",
          "click-1"
        ]
      }
    },
    {
      "id": "group-control",
      "element_type": "group",
      "title": "Control Flow",
      "color": "#8d8d8d",
      "position_x": 1488,
      "position_y": 456,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "end-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Extracts used vehicle listing data from CarSensor, including vehicle name, vehicle information, dealer, model year, mileage, repair history, inspection status, statutory maintenance, warranty, prices/plans, image URL, and listing URL. The supplied Octoparse sample detail/image URLs were expired/404, so this template starts from a live CarSensor Mercedes-Benz C-Class listing URL inferred from the sample data. Pagination is handled by clicking the real non-detail Next pagination link, including labels such as 次 or 次の30件, until no enabled Next link remains; all pages are appended into one CSV.",
      "color": "#f1c21b",
      "position_x": 80,
      "position_y": 20,
      "width": 480,
      "height": 160,
      "z_index": 22,
      "data": {}
    },
    {
      "id": "note-block-wait-for-element-1",
      "element_type": "note",
      "title": "Note: Wait for Element",
      "content": "Uses XPath `//div[contains(concat(' ', normalize-space(@class), ' '), ' cassetteWrap ')][.//a[contains(@href,'/u`. XPath breaks easily if DOM structure changes.",
      "color": "#ee5396",
      "position_x": 1040,
      "position_y": 200,
      "width": 340,
      "height": 133,
      "z_index": 22,
      "data": {
        "block_id": "wait-for-element-1"
      }
    },
    {
      "id": "note-block-structured-export-1",
      "element_type": "note",
      "title": "Note: Structured Export",
      "content": "Structured export with JS columns (車名, 車両情報, 販売店名, 年式, 走行距離). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 1400,
      "position_y": 200,
      "width": 340,
      "height": 122,
      "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 `(//a[not(contains(@href,'/usedcar/detail/')) and not(contains(@onclick,'/usedcar/detail/')) and not(contains(@href,'java`. True / False branches control which path runs next. Keep enough space between branches so both connector lines are visible.",
      "color": "#ee5396",
      "position_x": 1760,
      "position_y": 200,
      "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 `(//a[not(contains(@href,'/usedcar/detail/')) and not(contains(@onclick,'/usedcar/detail/')) and not(`. XPath breaks easily if DOM structure changes.",
      "color": "#ee5396",
      "position_x": 2120,
      "position_y": 540,
      "width": 340,
      "height": 133,
      "z_index": 22,
      "data": {
        "block_id": "click-1"
      }
    }
  ]
}