{
  "version": "1.0.0",
  "exported_at": "2026-06-03T00:05:00.000Z",
  "project": {
    "name": "Yahoo Japan Searching Scraper",
    "description": "Scrapes Yahoo! JAPAN web search results for the default keyword 番組, extracting keyword, title, link, and content/summary. Edit the Navigate URL query parameter p= to change the keyword. Pagination is handled by clicking the Japanese Next link until no more result pages are available. Yahoo may occasionally show CAPTCHA or bot checks requiring manual intervention. Result rows use the CSS selector a[href]:has(h3) instead of XPath so structured-export JavaScript columns do not fail.",
    "color": "bg-[#4589ff]",
    "template_id": "ai-generated"
  },
  "blocks": [
    {
      "block_id": "set-window-size-1",
      "block_type": "process",
      "title": "Set Window Size",
      "description": "Set browser window dimensions",
      "position_x": 120,
      "position_y": 260,
      "config": {
        "width": 1920,
        "height": 1080,
        "color": "bg-[#4589ff]"
      }
    },
    {
      "block_id": "navigate-1",
      "block_type": "process",
      "title": "Navigate",
      "description": "Go to a URL",
      "position_x": 480,
      "position_y": 260,
      "config": {
        "url": "https://search.yahoo.co.jp/search?p=%E7%95%AA%E7%B5%84",
        "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": 840,
      "position_y": 260,
      "config": {
        "timeout": 30
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 1200,
      "position_y": 260,
      "config": {
        "selector": "a[href]:has(h3)",
        "timeout": 30,
        "visible": true
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 1560,
      "position_y": 260,
      "config": {
        "rowSelector": "a[href]:has(h3)",
        "fileName": "1734_yahoo_search_results_clean.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "color": "bg-[#42be65]",
        "columns": [
          {
            "name": "keyword",
            "selector": "(() => new URLSearchParams(window.location.search).get('p') || document.querySelector('input[name=\"p\"]')?.value || '')()",
            "jsCode": "(() => new URLSearchParams(window.location.search).get('p') || document.querySelector('input[name=\"p\"]')?.value || '')()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "title",
            "selector": "h3",
            "attribute": "text"
          },
          {
            "name": "link",
            "selector": "",
            "attribute": "href"
          },
          {
            "name": "content",
            "selector": "(() => { const row = (typeof ROW !== 'undefined' ? ROW : null); if (!row) return ''; const title = (row.querySelector('h3')?.innerText || row.querySelector('h3')?.textContent || '').trim(); let root = row.parentElement; for (let i = 0; root && i < 8; i++) { const txt = (root.innerText || root.textContent || '').trim().replace(/\\s+/g, ' '); const hasTitle = title && txt.includes(title); const enoughText = txt.length > title.length + 35; const hasMultipleNodes = root.querySelectorAll && root.querySelectorAll('a, p, span, div').length >= 3; if (hasTitle && enoughText && hasMultipleNodes) break; root = root.parentElement; } root = root || row.parentElement || row; const badParts = ['Yahoo!検索', 'キャッシュ', '関連検索', '前へ', '次へ', '他の人はこちらも質問']; const candidates = Array.from(root.querySelectorAll ? root.querySelectorAll('p, span, div') : []).map(e => (e.innerText || e.textContent || '').trim().replace(/\\s+/g, ' ')).filter(t => t && t !== title && t.length > 18 && !/^https?:\\/\\//.test(t) && !badParts.some(b => t.includes(b))); const best = candidates.find(t => !t.includes(title) && t.length > 25) || candidates[0]; if (best) return best; const rootText = (root.innerText || root.textContent || '').trim().replace(/\\s+/g, ' '); return rootText.replace(title, '').replace(/https?:\\/\\/\\S+/g, '').trim(); })()",
            "jsCode": "(() => { const row = (typeof ROW !== 'undefined' ? ROW : null); if (!row) return ''; const title = (row.querySelector('h3')?.innerText || row.querySelector('h3')?.textContent || '').trim(); let root = row.parentElement; for (let i = 0; root && i < 8; i++) { const txt = (root.innerText || root.textContent || '').trim().replace(/\\s+/g, ' '); const hasTitle = title && txt.includes(title); const enoughText = txt.length > title.length + 35; const hasMultipleNodes = root.querySelectorAll && root.querySelectorAll('a, p, span, div').length >= 3; if (hasTitle && enoughText && hasMultipleNodes) break; root = root.parentElement; } root = root || row.parentElement || row; const badParts = ['Yahoo!検索', 'キャッシュ', '関連検索', '前へ', '次へ', '他の人はこちらも質問']; const candidates = Array.from(root.querySelectorAll ? root.querySelectorAll('p, span, div') : []).map(e => (e.innerText || e.textContent || '').trim().replace(/\\s+/g, ' ')).filter(t => t && t !== title && t.length > 18 && !/^https?:\\/\\//.test(t) && !badParts.some(b => t.includes(b))); const best = candidates.find(t => !t.includes(title) && t.length > 25) || candidates[0]; if (best) return best; const rootText = (root.innerText || root.textContent || '').trim().replace(/\\s+/g, ' '); return rootText.replace(title, '').replace(/https?:\\/\\/\\S+/g, '').trim(); })()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "element-exists-1",
      "block_type": "process",
      "title": "Element Exists",
      "description": "Check if element exists",
      "position_x": 1920,
      "position_y": 260,
      "config": {
        "selector": "//a[(contains(normalize-space(.), '次へ') or contains(@aria-label, '次') or contains(@title, '次') or @rel='next') and not(contains(@class, 'disabled'))]"
      }
    },
    {
      "block_id": "click-1",
      "block_type": "process",
      "title": "Click",
      "description": "Click on element",
      "position_x": 2280,
      "position_y": 560,
      "config": {
        "selector": "//a[(contains(normalize-space(.), '次へ') or contains(@aria-label, '次') or contains(@title, '次') or @rel='next') and not(contains(@class, 'disabled'))]",
        "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": 2640,
      "position_y": 560,
      "config": {
        "timeout": 30
      }
    },
    {
      "block_id": "wait-for-element-2",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 3000,
      "position_y": 560,
      "config": {
        "selector": "a[href]:has(h3)",
        "timeout": 30,
        "visible": true
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 3360,
      "position_y": 560,
      "config": {
        "duration": 2
      }
    },
    {
      "block_id": "end-1",
      "block_type": "output",
      "title": "End",
      "description": "Terminate execution flow",
      "position_x": 1920,
      "position_y": 900,
      "config": {
        "color": "bg-[#8d8d8d]"
      }
    }
  ],
  "connections": [
    {
      "from_block_id": "set-window-size-1",
      "from_connector_id": "right",
      "to_block_id": "navigate-1",
      "to_connector_id": "left"
    },
    {
      "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": "wait-for-element-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-element-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": "structured-export-1",
      "to_connector_id": "left"
    }
  ],
  "canvas_elements": [
    {
      "id": "group-entry",
      "element_type": "group",
      "title": "Entry & Setup",
      "color": "#4589ff",
      "position_x": 48,
      "position_y": 156,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "set-window-size-1"
        ]
      }
    },
    {
      "id": "group-load",
      "element_type": "group",
      "title": "Page Load",
      "color": "#08bdba",
      "position_x": 408,
      "position_y": 156,
      "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",
          "wait-for-element-2",
          "sleep-1"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 1488,
      "position_y": 156,
      "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": 156,
      "width": 680,
      "height": 596,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "element-exists-1",
          "click-1"
        ]
      }
    },
    {
      "id": "group-control",
      "element_type": "group",
      "title": "Control Flow",
      "color": "#8d8d8d",
      "position_x": 1848,
      "position_y": 796,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "end-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Scrapes Yahoo! JAPAN web search results for the default keyword 番組, extracting keyword, title, link, and content/summary. Edit the Navigate URL query parameter p= to change the keyword. Pagination is handled by clicking the Japanese Next link until no more result pages are available. Yahoo may occasionally show CAPTCHA or bot checks requiring manual intervention. Result rows use the CSS selector a[href]:has(h3) instead of XPath so structured-export JavaScript columns do not fail.",
      "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 (keyword, content). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 1760,
      "position_y": 240,
      "width": 340,
      "height": 119,
      "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[(contains(normalize-space(.), '次へ') or contains(@aria-label, '次') or contains(@title, '次') or @rel='next') and not(c`. 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": 240,
      "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[(contains(normalize-space(.), '次へ') or contains(@aria-label, '次') or contains(@title, '次') or @r`. XPath breaks easily if DOM structure changes.",
      "color": "#ee5396",
      "position_x": 2480,
      "position_y": 540,
      "width": 340,
      "height": 133,
      "z_index": 22,
      "data": {
        "block_id": "click-1"
      }
    }
  ]
}