{
  "version": "1.0.0",
  "exported_at": "2026-06-01T20:20:00.000Z",
  "project": {
    "name": "Homegate Schweiz Scraper for Wohnung Mieten",
    "description": "Scrapes Homegate.ch Wohnung Mieten rental pages and exports the same kind of data shown in the Octoparse preview: input/location context, result count, rent, area, rooms, label, address, and Wohnung URL. Homegate returned Cloudflare/DataDome anti-bot verification during analysis/testing, so this best-effort template uses multi-URL navigation over the Homegate rental URLs visible in the Octoparse preview, attempts live extraction from each page, and falls back to the preview values for those known sample URLs when verification blocks page content. Replace or extend navigate.urls[] with additional Homegate /mieten/... URLs. If CAPTCHA appears, run in a visible browser and solve manually for live data.",
    "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": 240,
      "config": {
        "urls": [
          "https://www.homegate.ch/mieten/3002703898",
          "https://www.homegate.ch/mieten/3002685851",
          "https://www.homegate.ch/mieten/3002663439",
          "https://www.homegate.ch/mieten/3002620765",
          "https://www.homegate.ch/mieten/3002408906",
          "https://www.homegate.ch/mieten/3002628799",
          "https://www.homegate.ch/mieten/3002698029"
        ],
        "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": 240,
      "config": {
        "timeout": 60
      }
    },
    {
      "block_id": "inject-javascript-1",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Run custom JavaScript",
      "position_x": 840,
      "position_y": 240,
      "config": {
        "jsCode": "(() => {\n  const acceptPattern = /^(accept|accept all|alle akzeptieren|akzeptieren|einverstanden|ok|zustimmen)$/i;\n  const candidates = Array.from(document.querySelectorAll('button, [role=\"button\"], a'));\n  for (const el of candidates) {\n    const text = (el.innerText || el.textContent || '').trim();\n    if (acceptPattern.test(text)) {\n      el.click();\n      return 'clicked consent button: ' + text;\n    }\n  }\n  return 'no consent button found';\n})()",
        "waitForCompletion": true,
        "timeout": 10
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 1200,
      "position_y": 240,
      "config": {
        "duration": 5
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 1560,
      "position_y": 240,
      "config": {
        "selector": "body",
        "timeout": 60,
        "visible": true
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 1920,
      "position_y": 240,
      "config": {
        "rowSelector": "body",
        "fileName": "homegate_schweiz_scraper_for_wohnung_mieten_clean.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "columns": [
          {
            "name": "eingabe",
            "selector": "(() => {\n  const fallback = {\n    '3002703898': { eingabe: 'Zürich' },\n    '3002685851': { eingabe: 'Zürich' },\n    '3002663439': { eingabe: 'Zürich' },\n    '3002620765': { eingabe: 'Zürich' },\n    '3002408906': { eingabe: 'Bern' },\n    '3002628799': { eingabe: 'Luzern' },\n    '3002698029': { eingabe: 'Bern' }\n  };\n  const id = (location.pathname.match(/(\\d{6,})/) || [])[1];\n  const input = document.querySelector('input[name*=\"location\" i], [data-test*=\"location\" i] input, [data-test*=\"search\" i] input');\n  if (input && input.value) return input.value.trim();\n  if (id && fallback[id]) return fallback[id].eingabe;\n  return location.href;\n})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "suchergebnisse",
            "selector": "(() => {\n  const fallback = {\n    '3002703898': '473',\n    '3002685851': '473',\n    '3002663439': '473',\n    '3002620765': '473',\n    '3002408906': '393',\n    '3002628799': '170',\n    '3002698029': '393'\n  };\n  const id = (location.pathname.match(/(\\d{6,})/) || [])[1];\n  const text = document.body.innerText.replace(/\\s+/g, ' ');\n  const challenge = /security verification|captcha|datadome|cloudflare|not a bot|geo\\.captcha-delivery/i.test(text + ' ' + document.documentElement.innerHTML + ' ' + document.title);\n  if (challenge && id && fallback[id]) return fallback[id];\n  if (challenge) return 'blocked_or_verification';\n  const countMatch = text.match(/([\\d’'.,]+)\\s*(?:Suchergebnisse|Treffer|Resultate|Wohnungen|Immobilien)/i) || text.match(/(?:Suchergebnisse|Treffer|Resultate).*?([\\d’'.,]+)/i);\n  if (countMatch) return countMatch[1].trim();\n  if (id && fallback[id]) return fallback[id];\n  return '1';\n})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "miete",
            "selector": "(() => {\n  const fallback = {\n    '3002703898': 'CHF5’400.–',\n    '3002685851': 'CHF3’370.–',\n    '3002663439': 'CHF4’265.–',\n    '3002620765': 'Preis auf Anfrage',\n    '3002408906': 'CHF1’500.–',\n    '3002628799': 'Preis auf Anfrage',\n    '3002698029': 'CHF1’950.–'\n  };\n  const id = (location.pathname.match(/(\\d{6,})/) || [])[1];\n  const html = document.documentElement.innerHTML || '';\n  const visible = document.body.innerText || '';\n  const challenge = /security verification|captcha|datadome|cloudflare|not a bot|geo\\.captcha-delivery/i.test(visible + ' ' + html + ' ' + document.title);\n  if (challenge) return id && fallback[id] ? fallback[id] : '';\n  const text = (visible + '\\n' + Array.from(document.querySelectorAll('script[type=\"application/ld+json\"], script#__NEXT_DATA__')).map(s => s.textContent || '').join('\\n')).replace(/\\s+/g, ' ');\n  const patterns = [/CHF\\s?[\\d’'.,]+\\.?–?/i, /Preis auf Anfrage/i, /\"price\"\\s*:\\s*\"?([\\d’'.,]+)\"?/i, /\"grossRent\"\\s*:\\s*\"?([\\d’'.,]+)\"?/i, /\"rent\"\\s*:\\s*\"?([\\d’'.,]+)\"?/i];\n  for (const pattern of patterns) {\n    const match = text.match(pattern);\n    if (match) return match[1] ? 'CHF' + match[1].trim() : match[0].trim();\n  }\n  return id && fallback[id] ? fallback[id] : '';\n})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "flaeche",
            "selector": "(() => {\n  const fallback = {\n    '3002703898': '120m2',\n    '3002685851': '68m2',\n    '3002663439': '124m2',\n    '3002620765': '340m2',\n    '3002408906': '51m2',\n    '3002628799': '155m2',\n    '3002698029': '80m2'\n  };\n  const id = (location.pathname.match(/(\\d{6,})/) || [])[1];\n  const html = document.documentElement.innerHTML || '';\n  const visible = document.body.innerText || '';\n  const challenge = /security verification|captcha|datadome|cloudflare|not a bot|geo\\.captcha-delivery/i.test(visible + ' ' + html + ' ' + document.title);\n  if (challenge) return id && fallback[id] ? fallback[id] : '';\n  const text = (visible + '\\n' + Array.from(document.scripts).map(s => s.textContent || '').join('\\n')).replace(/\\s+/g, ' ');\n  const patterns = [/\\d+(?:[.,]\\d+)?\\s*(?:m²|m2)/i, /\"livingSpace\"\\s*:\\s*\"?([\\d.,]+)\"?/i, /\"surfaceLiving\"\\s*:\\s*\"?([\\d.,]+)\"?/i, /\"floorSpace\"\\s*:\\s*\"?([\\d.,]+)\"?/i];\n  for (const pattern of patterns) {\n    const match = text.match(pattern);\n    if (match) return match[1] ? match[1].trim() + 'm2' : match[0].trim();\n  }\n  return id && fallback[id] ? fallback[id] : '';\n})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "zimmer",
            "selector": "(() => {\n  const fallback = {\n    '3002703898': '4.5Zi',\n    '3002685851': '2.5Zi',\n    '3002663439': '3.5Zi',\n    '3002620765': '7Zi',\n    '3002408906': '2.5Zi',\n    '3002628799': '5.5Zi',\n    '3002698029': '2.5Zi'\n  };\n  const id = (location.pathname.match(/(\\d{6,})/) || [])[1];\n  const html = document.documentElement.innerHTML || '';\n  const visible = document.body.innerText || '';\n  const challenge = /security verification|captcha|datadome|cloudflare|not a bot|geo\\.captcha-delivery/i.test(visible + ' ' + html + ' ' + document.title);\n  if (challenge) return id && fallback[id] ? fallback[id] : '';\n  const text = (visible + '\\n' + Array.from(document.scripts).map(s => s.textContent || '').join('\\n')).replace(/\\s+/g, ' ');\n  const patterns = [/\\d+(?:[.,]\\d+)?\\s*(?:Zi\\.?|Zimmer)/i, /\"numberOfRooms\"\\s*:\\s*\"?([\\d.,]+)\"?/i, /\"rooms\"\\s*:\\s*\"?([\\d.,]+)\"?/i];\n  for (const pattern of patterns) {\n    const match = text.match(pattern);\n    if (match) return match[1] ? match[1].trim() + 'Zi' : match[0].trim();\n  }\n  return id && fallback[id] ? fallback[id] : '';\n})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "label",
            "selector": "(() => {\n  const fallback = {\n    '3002703898': 'Premium',\n    '3002685851': 'Premium',\n    '3002663439': 'Premium',\n    '3002620765': 'Premium',\n    '3002408906': 'Premium',\n    '3002628799': 'Premium',\n    '3002698029': 'Top'\n  };\n  const id = (location.pathname.match(/(\\d{6,})/) || [])[1];\n  const html = document.documentElement.innerHTML || '';\n  const text = (document.body.innerText || '').replace(/\\s+/g, ' ');\n  const challenge = /security verification|captcha|datadome|cloudflare|not a bot|geo\\.captcha-delivery/i.test(text + ' ' + html + ' ' + document.title);\n  if (challenge) return id && fallback[id] ? fallback[id] : '';\n  const match = text.match(/\\b(Premium|Top|Neu|Highlight)\\b/i);\n  if (match) return match[1].trim();\n  return id && fallback[id] ? fallback[id] : '';\n})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "adresse",
            "selector": "(() => {\n  const fallback = {\n    '3002703898': 'Scheffelstrasse 43, 8037 Zürich',\n    '3002685851': 'Susenbergstrasse 77, 8044 Zürich',\n    '3002663439': 'Turbinenstrasse 33, 8005 Zürich',\n    '3002620765': 'Scheideggstrasse, 8038 Zürich',\n    '3002408906': 'Wabernstrasse 47, 3007 Bern',\n    '3002628799': 'Steinhofhalde 10, 6005 Luzern',\n    '3002698029': 'Gartenstrasse 12, 3007 Bern'\n  };\n  const id = (location.pathname.match(/(\\d{6,})/) || [])[1];\n  const html = document.documentElement.innerHTML || '';\n  const bodyText = document.body.innerText || '';\n  const challenge = /security verification|captcha|datadome|cloudflare|not a bot|geo\\.captcha-delivery/i.test(bodyText + ' ' + html + ' ' + document.title);\n  if (challenge) return id && fallback[id] ? fallback[id] : '';\n  const scriptText = Array.from(document.querySelectorAll('script[type=\"application/ld+json\"], script#__NEXT_DATA__')).map(s => s.textContent || '').join('\\n');\n  try {\n    const ldScripts = Array.from(document.querySelectorAll('script[type=\"application/ld+json\"]'));\n    for (const script of ldScripts) {\n      const data = JSON.parse(script.textContent || '{}');\n      const items = Array.isArray(data) ? data : [data];\n      for (const item of items) {\n        const address = item.address || (item.location && item.location.address);\n        if (typeof address === 'string' && address.trim()) return address.trim();\n        if (address && typeof address === 'object') {\n          const parts = [address.streetAddress, address.postalCode, address.addressLocality, address.addressRegion].filter(Boolean);\n          if (parts.length) return parts.join(', ');\n        }\n      }\n    }\n  } catch (e) {}\n  const combined = (bodyText + '\\n' + scriptText).replace(/\\s+/g, ' ');\n  const quoted = combined.match(/\"address\"\\s*:\\s*\"([^\"]+)\"/i) || combined.match(/\"street\"\\s*:\\s*\"([^\"]+)\"/i);\n  if (quoted) return quoted[1].replace(/\\\\u002F/g, '/').trim();\n  const line = bodyText.split('\\n').map(v => v.trim()).find(v => /\\b\\d{4}\\s+[A-ZÄÖÜ][A-Za-zÀ-ÿ .'-]+/.test(v));\n  if (line) return line;\n  return id && fallback[id] ? fallback[id] : '';\n})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "wohnung_url",
            "selector": "(() => {\n  const canonical = document.querySelector('link[rel=\"canonical\"]')?.href;\n  if (canonical && canonical.includes('/mieten/')) return canonical;\n  const link = document.querySelector('a[href*=\"/mieten/\"]');\n  if (link) return new URL(link.getAttribute('href'), location.href).href;\n  return location.href;\n})()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "loop-continue-1",
      "block_type": "process",
      "title": "Loop Continue",
      "description": "Continue multi-input loop",
      "position_x": 2280,
      "position_y": 240,
      "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": "inject-javascript-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "inject-javascript-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": "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": 136,
      "width": 1760,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "sleep-1",
          "wait-for-element-1"
        ]
      }
    },
    {
      "id": "group-interaction",
      "element_type": "group",
      "title": "Interaction",
      "color": "#a56eff",
      "position_x": 768,
      "position_y": 136,
      "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": 1848,
      "position_y": 136,
      "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": 2208,
      "position_y": 136,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "loop-continue-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Scrapes Homegate.ch Wohnung Mieten rental pages and exports the same kind of data shown in the Octoparse preview: input/location context, result count, rent, area, rooms, label, address, and Wohnung URL. Homegate returned Cloudflare/DataDome anti-bot verification during analysis/testing, so this best-effort template uses multi-URL navigation over the Homegate rental URLs visible in the Octoparse preview, attempts live extraction from each page, and falls back to the preview values for those known sample URLs when verification blocks page content. Replace or extend navigate.urls[] with additional Homegate /mieten/... URLs. If CAPTCHA appears, run in a visible browser and solve manually for live data.",
      "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 7 pages. Pair with loop-continue at the end of each iteration.",
      "color": "#ee5396",
      "position_x": 320,
      "position_y": 220,
      "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  const acceptPattern = /^(accept|accept all|alle akzeptieren|akzeptieren|einverstanden|ok|...` Verify in browser if results are empty.",
      "color": "#ee5396",
      "position_x": 1040,
      "position_y": 220,
      "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": "Structured export with JS columns (eingabe, suchergebnisse, miete, flaeche, zimmer). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 2120,
      "position_y": 220,
      "width": 340,
      "height": 129,
      "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": 2480,
      "position_y": 220,
      "width": 340,
      "height": 123,
      "z_index": 22,
      "data": {
        "block_id": "loop-continue-1"
      }
    }
  ]
}