{
  "version": "1.0.0",
  "exported_at": "2026-05-31T13:10:00.000Z",
  "project": {
    "name": "Town Work Job Scraper",
    "description": "Scrapes Town Work job listings from the provided search URL, extracting job URL, title, employer/company, salary, employment type, access/location text, and placeholder columns matching the Octoparse preview. Pagination is handled by marking the valid Japanese Next link (次へ), navigating to it with JavaScript, and stopping when no valid next link remains or after a safe 95-page cap based on the detected 1,887 results. Note: supplied sample detail URLs are expired/404, so detail-only fields are best-effort placeholders from listing pages.",
    "color": "bg-[#42be65]",
    "template_id": "ai-generated-townwork-job-scraper"
  },
  "blocks": [
    {
      "block_id": "navigate-1",
      "block_type": "process",
      "title": "Navigate",
      "description": "Go to a URL",
      "position_x": 120,
      "position_y": 220,
      "config": {
        "url": "https://townwork.net/prefectures/fukuoka/job_search/?st=6899&st=8226&st=4998&st=7613&st=8774&st=231&st=80&st=9150&st=8340&st=5263&st=1240&st=1286",
        "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": "a[href^=\"/viewjob/jobid_\"], a[href*=\"/viewjob/jobid_\"]",
        "timeout": 30,
        "visible": true
      }
    },
    {
      "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": "a[href^=\"/viewjob/jobid_\"], a[href*=\"/viewjob/jobid_\"]",
        "fileName": "townwork-job-scraper.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "columns": [
          {
            "name": "Original_Input_URL",
            "selector": "(() => { const u = new URL(window.location.href); u.searchParams.delete('cursor'); return u.href; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "詳細URL",
            "selector": "ROW.href || ROW.getAttribute('href') || ''",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "タイトル",
            "selector": "[class*=\"styles_title__\"]",
            "attribute": "text"
          },
          {
            "name": "掲載先",
            "selector": "[class*=\"styles_employerName__\"]",
            "attribute": "text"
          },
          {
            "name": "給料",
            "selector": "[class*=\"styles_salaryText__\"]",
            "attribute": "text"
          },
          {
            "name": "雇用形態",
            "selector": "[class*=\"styles_captionText__\"]",
            "attribute": "text"
          },
          {
            "name": "勤務地",
            "selector": "[class*=\"styles_accessText__\"]",
            "attribute": "text"
          },
          {
            "name": "アクセス_交通",
            "selector": "[class*=\"styles_accessText__\"]",
            "attribute": "text"
          },
          {
            "name": "勤務時間",
            "selector": "''",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "休日休暇",
            "selector": "''",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "待遇_福利厚生",
            "selector": "''",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "職種_仕事内容",
            "selector": "ROW.innerText || ''",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "会社名",
            "selector": "[class*=\"styles_employerName__\"]",
            "attribute": "text"
          },
          {
            "name": "住所",
            "selector": "''",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "電話番号",
            "selector": "''",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "inject-javascript-1",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Run custom JavaScript on the page",
      "position_x": 1560,
      "position_y": 220,
      "config": {
        "jsCode": "(() => {\n  const MAX_PAGES = 95;\n  const key = 'uscraper_townwork_pages_extracted';\n  const currentCount = parseInt(sessionStorage.getItem(key) || '0', 10) + 1;\n  sessionStorage.setItem(key, String(currentCount));\n\n  document.querySelectorAll('[data-uscraper-next]').forEach(el => el.removeAttribute('data-uscraper-next'));\n\n  if (currentCount >= MAX_PAGES) {\n    console.log('UScraper TownWork: reached page cap', currentCount, MAX_PAGES);\n    return 'page-cap-reached';\n  }\n\n  const current = new URL(window.location.href);\n  const candidates = Array.from(document.querySelectorAll('a')).filter(a => {\n    const text = (a.textContent || '').trim();\n    if (text !== '次へ') return false;\n    const href = a.href || '';\n    if (!href || !href.includes('townwork.net') || !href.includes('/job_search/') || !href.includes('cursor=')) return false;\n    let next;\n    try { next = new URL(href); } catch (e) { return false; }\n    if (next.href === current.href) return false;\n    if (next.pathname.includes('[prefecture_id]') || next.pathname.includes('[[...path]]')) return false;\n    return true;\n  });\n\n  const next = candidates[candidates.length - 1];\n  if (next) {\n    next.setAttribute('data-uscraper-next', 'true');\n    console.log('UScraper TownWork: marked next page', currentCount, next.href);\n    return next.href;\n  }\n  console.log('UScraper TownWork: no valid next link found');\n  return 'no-next';\n})()",
        "waitForCompletion": true,
        "timeout": 10
      }
    },
    {
      "block_id": "element-exists-1",
      "block_type": "process",
      "title": "Element Exists",
      "description": "Check if element exists",
      "position_x": 1920,
      "position_y": 220,
      "config": {
        "selector": "a[data-uscraper-next=\"true\"]"
      }
    },
    {
      "block_id": "inject-javascript-2",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Run custom JavaScript on the page",
      "position_x": 1920,
      "position_y": 520,
      "config": {
        "jsCode": "(() => {\n  const next = document.querySelector('a[data-uscraper-next=\"true\"]');\n  if (!next || !next.href) return 'no-next-to-navigate';\n  const href = next.href;\n  console.log('UScraper TownWork: navigating to next page', href);\n  window.location.href = href;\n  return href;\n})()",
        "waitForCompletion": true,
        "timeout": 10
      }
    },
    {
      "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": 520,
      "config": {
        "timeout": 30
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 2640,
      "position_y": 520,
      "config": {
        "duration": 2
      }
    },
    {
      "block_id": "wait-for-element-2",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 3000,
      "position_y": 520,
      "config": {
        "selector": "a[href^=\"/viewjob/jobid_\"], a[href*=\"/viewjob/jobid_\"]",
        "timeout": 30,
        "visible": true
      }
    },
    {
      "block_id": "end-1",
      "block_type": "output",
      "title": "End",
      "description": "Terminate execution flow",
      "position_x": 1920,
      "position_y": 840,
      "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": "structured-export-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "structured-export-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": "element-exists-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-exists-1",
      "from_connector_id": "true",
      "to_block_id": "inject-javascript-2",
      "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": "inject-javascript-2",
      "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-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"
    }
  ],
  "canvas_elements": [
    {
      "id": "group-load",
      "element_type": "group",
      "title": "Page Load",
      "color": "#08bdba",
      "position_x": 48,
      "position_y": 116,
      "width": 3200,
      "height": 596,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "wait-for-element-1",
          "wait-for-page-load-2",
          "sleep-1",
          "wait-for-element-2"
        ]
      }
    },
    {
      "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-interaction",
      "element_type": "group",
      "title": "Interaction",
      "color": "#a56eff",
      "position_x": 1488,
      "position_y": 116,
      "width": 680,
      "height": 596,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "inject-javascript-1",
          "inject-javascript-2"
        ]
      }
    },
    {
      "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": [
          "element-exists-1"
        ]
      }
    },
    {
      "id": "group-control",
      "element_type": "group",
      "title": "Control Flow",
      "color": "#8d8d8d",
      "position_x": 1848,
      "position_y": 736,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "end-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Scrapes Town Work job listings from the provided search URL, extracting job URL, title, employer/company, salary, employment type, access/location text, and placeholder columns matching the Octoparse preview. Pagination is handled by marking the valid Japanese Next link (次へ), navigating to it with JavaScript, and stopping when no valid next link remains or after a safe 95-page cap based on the detected 1,887 results. Note: supplied sample detail URLs are expired/404, so detail-only fields are best-effort placeholders from listing pages.",
      "color": "#f1c21b",
      "position_x": 80,
      "position_y": 20,
      "width": 480,
      "height": 160,
      "z_index": 22,
      "data": {}
    },
    {
      "id": "note-block-structured-export-1",
      "element_type": "note",
      "title": "Note: Structured Export",
      "content": "Structured export with JS columns (Original_Input_URL, 詳細URL, 勤務時間, 休日休暇, 待遇_福利厚生). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 1400,
      "position_y": 200,
      "width": 340,
      "height": 129,
      "z_index": 22,
      "data": {
        "block_id": "structured-export-1"
      }
    },
    {
      "id": "note-block-inject-javascript-1",
      "element_type": "note",
      "title": "Note: Inject JavaScript",
      "content": "Runs custom JavaScript in the page: `(() => {\n  const MAX_PAGES = 95;\n  const key = 'uscraper_townwork_pages_extracted';\n  const currentC...` Verify in browser if results are empty.",
      "color": "#ee5396",
      "position_x": 1760,
      "position_y": 200,
      "width": 340,
      "height": 140,
      "z_index": 22,
      "data": {
        "block_id": "inject-javascript-1"
      }
    },
    {
      "id": "note-block-element-exists-1",
      "element_type": "note",
      "title": "Note: Element Exists",
      "content": "Condition block: checks `a[data-uscraper-next=\"true\"]`. True / False branches control which path runs next. Keep enough space between branches so both connector lines are visible.",
      "color": "#ee5396",
      "position_x": 2120,
      "position_y": 200,
      "width": 340,
      "height": 139,
      "z_index": 22,
      "data": {
        "block_id": "element-exists-1"
      }
    },
    {
      "id": "note-block-inject-javascript-2",
      "element_type": "note",
      "title": "Note: Inject JavaScript",
      "content": "Runs custom JavaScript in the page: `(() => {\n  const next = document.querySelector('a[data-uscraper-next=\"true\"]');\n  if (!next || !next...` Verify in browser if results are empty.",
      "color": "#ee5396",
      "position_x": 2120,
      "position_y": 500,
      "width": 340,
      "height": 140,
      "z_index": 22,
      "data": {
        "block_id": "inject-javascript-2"
      }
    }
  ]
}