{
  "version": "1.0.0",
  "exported_at": "2026-06-02T23:20:00.000Z",
  "project": {
    "name": "Kamome Job Listings Scraper",
    "description": "Scrapes Kamome.cn job detail pages by URL list, equivalent to the Octoparse Kamome Job Listings Scraper. The template visits each supplied /job_search/{id}/ URL, waits for the job detail page, extracts company name, job position, employment type, salary, description, qualifications, location, working time, job ID, and page URL, then appends all rows to one CSV. Navigation uses a multi-URL loop; edit the navigate.urls list to add more Kamome job detail URLs.",
    "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://kamome.cn/job_search/105557/",
          "https://kamome.cn/job_search/109270/",
          "https://kamome.cn/job_search/115863/",
          "https://kamome.cn/job_search/88902/",
          "https://kamome.cn/job_search/115771/",
          "https://kamome.cn/job_search/115778/"
        ],
        "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": 456,
      "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": 792,
      "position_y": 220,
      "config": {
        "selector": "h2",
        "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": 1128,
      "position_y": 220,
      "config": {
        "rowSelector": "body",
        "fileName": "kamome-job-listings-scraper.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "color": "bg-[#42be65]",
        "columns": [
          {
            "name": "company_name",
            "selector": "(()=>{const meta=Array.from(document.getElementsByTagName('meta')).find(m=>m.getAttribute('property')==='og:title');const s=(meta&&meta.content)||document.title||'';let m=s.match(/の求人（([^）]+)）/);if(m)return m[1].trim();const text=(document.body.innerText||'').replace(/\\s+/g,' ');m=text.match(/「([^」]+)」の求人詳細/);return m?m[1].trim():'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "job_id",
            "selector": "(()=>{const h=Array.from(document.querySelectorAll('h2')).map(e=>e.innerText.trim()).find(x=>/（ID:\\d+）/.test(x));const m=h&&h.match(/ID:(\\d+)/);return m?m[1]:'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "job_title",
            "selector": "(()=>{const h=Array.from(document.querySelectorAll('h2')).map(e=>e.innerText.trim()).find(x=>/（ID:\\d+）/.test(x));return h?h.replace(/\\s*（ID:\\d+）/,'').trim():'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "job_position",
            "selector": "(()=>{const t=e=>(e?e.innerText||e.textContent||'':'').replace(/\\s+/g,' ').trim();const f=(labs,stops)=>{labs=[].concat(labs);stops=stops||[];for(const r of ROW.querySelectorAll('tr,dl')){const c=Array.from(r.children||[]);if(c.length>=2&&labs.some(l=>t(c[0]).replace('：','')===l||t(c[0]).includes(l))){const v=c.slice(1).map(t).join(' ').trim();if(v)return v;}}const all=t(ROW);let hit=null;for(const l of labs){const i=all.indexOf(l);if(i>=0&&(!hit||i<hit.i))hit={l,i};}if(!hit)return '';let s=hit.i+hit.l.length;let e=all.length;for(const p of stops){const j=all.indexOf(p,s);if(j>=0&&j<e)e=j;}return all.slice(s,e).replace(/^[:：\\s]+/,'').trim();};return f('募集ポジション',['雇用形態','仕事の内容','仕事内容','応募資格','勤務地']);})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "employment_type",
            "selector": "(()=>{const t=e=>(e?e.innerText||e.textContent||'':'').replace(/\\s+/g,' ').trim();const f=(labs,stops)=>{labs=[].concat(labs);stops=stops||[];for(const r of ROW.querySelectorAll('tr,dl')){const c=Array.from(r.children||[]);if(c.length>=2&&labs.some(l=>t(c[0]).replace('：','')===l||t(c[0]).includes(l))){const v=c.slice(1).map(t).join(' ').trim();if(v)return v;}}const all=t(ROW);let hit=null;for(const l of labs){const i=all.indexOf(l);if(i>=0&&(!hit||i<hit.i))hit={l,i};}if(!hit)return '';let s=hit.i+hit.l.length;let e=all.length;for(const p of stops){const j=all.indexOf(p,s);if(j>=0&&j<e)e=j;}return all.slice(s,e).replace(/^[:：\\s]+/,'').trim();};return f('雇用形態',['仕事の内容','仕事内容','応募資格','勤務地']);})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "salary",
            "selector": "(()=>{const text=(document.body.innerText||'').replace(/\\s+/g,' ');const m=text.match(/給与[:：]\\s*([^ ]*元(?:[＋+][^ ]+)?)|給与[:：]\\s*(応相談(?:[＋+][^ ]+)?)|【給与】\\s*([^ ]+元[^ ]*)/);return m?(m[1]||m[2]||m[3]||'').trim():'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "job_description",
            "selector": "(()=>{const t=e=>(e?e.innerText||e.textContent||'':'').replace(/\\s+/g,' ').trim();const f=(labs,stops)=>{labs=[].concat(labs);stops=stops||[];for(const r of ROW.querySelectorAll('tr,dl')){const c=Array.from(r.children||[]);if(c.length>=2&&labs.some(l=>t(c[0]).replace('：','')===l||t(c[0]).includes(l))){const v=c.slice(1).map(t).join(' ').trim();if(v)return v;}}const all=t(ROW);let hit=null;for(const l of labs){const i=all.indexOf(l);if(i>=0&&(!hit||i<hit.i))hit={l,i};}if(!hit)return '';let s=hit.i+hit.l.length;let e=all.length;for(const p of stops){const j=all.indexOf(p,s);if(j>=0&&j<e)e=j;}return all.slice(s,e).replace(/^[:：\\s]+/,'').trim();};return f(['仕事の内容','仕事内容'],['応募資格','歓迎するネイティブ言語','勤務開始日','勤務地']);})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "qualifications",
            "selector": "(()=>{const t=e=>(e?e.innerText||e.textContent||'':'').replace(/\\s+/g,' ').trim();const f=(labs,stops)=>{labs=[].concat(labs);stops=stops||[];for(const r of ROW.querySelectorAll('tr,dl')){const c=Array.from(r.children||[]);if(c.length>=2&&labs.some(l=>t(c[0]).replace('：','')===l||t(c[0]).includes(l))){const v=c.slice(1).map(t).join(' ').trim();if(v)return v;}}const all=t(ROW);let hit=null;for(const l of labs){const i=all.indexOf(l);if(i>=0&&(!hit||i<hit.i))hit={l,i};}if(!hit)return '';let s=hit.i+hit.l.length;let e=all.length;for(const p of stops){const j=all.indexOf(p,s);if(j>=0&&j<e)e=j;}return all.slice(s,e).replace(/^[:：\\s]+/,'').trim();};return f('応募資格',['歓迎するネイティブ言語','勤務開始日','勤務地','給与・待遇・ビザサポート']);})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "location",
            "selector": "(()=>{const t=e=>(e?e.innerText||e.textContent||'':'').replace(/\\s+/g,' ').trim();const f=(labs,stops)=>{labs=[].concat(labs);stops=stops||[];for(const r of ROW.querySelectorAll('tr,dl')){const c=Array.from(r.children||[]);if(c.length>=2&&labs.some(l=>t(c[0]).replace('：','')===l||t(c[0]).includes(l))){const v=c.slice(1).map(t).join(' ').trim();if(v)return v;}}const all=t(ROW);let hit=null;for(const l of labs){const i=all.indexOf(l);if(i>=0&&(!hit||i<hit.i))hit={l,i};}if(!hit)return '';let s=hit.i+hit.l.length;let e=all.length;for(const p of stops){const j=all.indexOf(p,s);if(j>=0&&j<e)e=j;}return all.slice(s,e).replace(/^[:：\\s]+/,'').trim();};return f('勤務地',['給与・待遇・ビザサポート','応募方法','おすすめキーワード']);})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "working_hours",
            "selector": "(()=>{const t=e=>(e?e.innerText||e.textContent||'':'').replace(/\\s+/g,' ').trim();const f=(labs,stops)=>{labs=[].concat(labs);stops=stops||[];for(const r of ROW.querySelectorAll('tr,dl')){const c=Array.from(r.children||[]);if(c.length>=2&&labs.some(l=>t(c[0]).replace('：','')===l||t(c[0]).includes(l))){const v=c.slice(1).map(t).join(' ').trim();if(v)return v;}}const all=t(ROW);let hit=null;for(const l of labs){const i=all.indexOf(l);if(i>=0&&(!hit||i<hit.i))hit={l,i};}if(!hit)return '';let s=hit.i+hit.l.length;let e=all.length;for(const p of stops){const j=all.indexOf(p,s);if(j>=0&&j<e)e=j;}return all.slice(s,e).replace(/^[:：\\s]+/,'').trim();};return f(['勤務時間・休日','勤務時間'],['応募方法','おすすめキーワード','会社HP']);})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "page_url",
            "selector": "location.href",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "loop-continue-1",
      "block_type": "process",
      "title": "Loop Continue",
      "description": "Continue multi-input loop",
      "position_x": 1464,
      "position_y": 220,
      "config": {
        "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": "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": 992,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "wait-for-element-1"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 1056,
      "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": 1392,
      "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 Kamome.cn job detail pages by URL list, equivalent to the Octoparse Kamome Job Listings Scraper. The template visits each supplied /job_search/{id}/ URL, waits for the job detail page, extracts company name, job position, employment type, salary, description, qualifications, location, working time, job ID, and page URL, then appends all rows to one CSV. Navigation uses a multi-URL loop; edit the navigate.urls list to add more Kamome job detail URLs.",
      "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 6 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-structured-export-1",
      "element_type": "note",
      "title": "Note: Structured Export",
      "content": "Structured export with JS columns (company_name, job_id, job_title, job_position, employment_type). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 1328,
      "position_y": 200,
      "width": 340,
      "height": 134,
      "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": 1664,
      "position_y": 200,
      "width": 340,
      "height": 123,
      "z_index": 22,
      "data": {
        "block_id": "loop-continue-1"
      }
    }
  ]
}