{
  "version": "1.0.0",
  "exported_at": "2026-06-02T00:00:00.000Z",
  "project": {
    "name": "Localch Lead Scraper via URL",
    "description": "Scrapes local.ch listing/search pages such as https://www.local.ch/fr/s/restaurant?rid=6b1d51 and exports lead fields matching the Octoparse template: title, rating, address, telephone, fax, mobile/portable, email, and website. The workflow dismisses cookie prompts, clicks visible show-contact controls inside result cards where possible, fetches local.ch detail pages as a fallback, exports normalized rows to CSV, and uses a guarded pagination loop. If local.ch presents CAPTCHA, bot protection, or hides contact details behind anti-scraping controls, manual intervention may be required.",
    "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": {
        "url": "https://www.local.ch/fr/s/restaurant?rid=6b1d51",
        "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": 30
      }
    },
    {
      "block_id": "inject-javascript-1",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Run custom JavaScript on the page",
      "position_x": 840,
      "position_y": 240,
      "config": {
        "jsCode": "(() => { const norm = s => (s || '').replace(/\\s+/g, ' ').trim().toLowerCase(); Array.from(document.querySelectorAll('button,a,[role=button]')).filter(el => /^(accept|accepter|tout accepter|alle akzeptieren|accetta|acconsento|ok|j'accepte)$/.test(norm(el.textContent)) || /accept|consent|cookie/.test(norm((el.id || '') + ' ' + (el.className || '') + ' ' + (el.getAttribute('aria-label') || '')))).slice(0, 3).forEach(el => { try { el.click(); } catch(e) {} }); })();",
        "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": 2
      }
    },
    {
      "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": 30,
        "visible": true
      }
    },
    {
      "block_id": "inject-javascript-2",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Run custom JavaScript on the page",
      "position_x": 1920,
      "position_y": 240,
      "config": {
        "jsCode": "(() => { const norm = s => (s || '').replace(/\\s+/g, ' ').trim(); const compact = s => norm(s).replace(/\\s+/g, ' '); const decodeEscapes = s => String(s || '').replace(/\\\\u([0-9a-fA-F]{4})/g, (_, h) => String.fromCharCode(parseInt(h, 16))).replace(/\\\\\\//g, '/').replace(/&amp;/g, '&').replace(/&quot;/g, '\"').replace(/&#x2F;/g, '/'); const old = document.getElementById('uscraper-localch-results'); if (old) old.remove(); const oldDone = document.getElementById('uscraper-localch-done'); if (oldDone) oldDone.remove(); const oldNext = document.getElementById('uscraper-localch-next-button'); if (oldNext) oldNext.remove(); const root = document.createElement('div'); root.id = 'uscraper-localch-results'; root.style.display = 'none'; document.body.appendChild(root); const markDone = () => { if (!document.getElementById('uscraper-localch-done')) { const d = document.createElement('div'); d.id = 'uscraper-localch-done'; d.style.display = 'none'; d.textContent = 'done'; document.body.appendChild(d); } }; if (!window.__uscraperLocalchSeen) window.__uscraperLocalchSeen = new Set(); const qsa = (sel, base = document) => { try { return Array.from(base.querySelectorAll(sel)); } catch(e) { return []; } }; const phoneRe = /(?:\\+41\\s?(?:\\(0\\)\\s?)?|0)\\d{1,2}[\\s.\\-/]?\\d{3}[\\s.\\-/]?\\d{2}[\\s.\\-/]?\\d{2}/g; const emailRe = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/ig; const detailRe = /\\/([a-z]{2})\\/d\\//; const cleanPhone = s => norm(String(s || '')).replace(/^tel:/i, '').replace(/[^+0-9]/g, ''); const unique = arr => Array.from(new Set(arr.filter(Boolean))); const textOf = node => norm(node ? (node.innerText || node.textContent || '') : ''); const getTitle = el => { const t = el.querySelector('h1,h2,h3,[data-testid*=title i]'); if (t && textOf(t).length > 1) return textOf(t); const link = qsa('a[href]', el).find(a => detailRe.test(a.href) && textOf(a).length > 1); return textOf(link); }; const detailHref = el => { const a = qsa('a[href]', el).find(a => detailRe.test(a.href)); return a ? new URL(a.getAttribute('href'), location.href).href : ''; }; const score = el => { const text = textOf(el); const hasDetail = qsa('a[href]', el).some(a => detailRe.test(a.href)); const ph = phoneRe.test(text); phoneRe.lastIndex = 0; const em = emailRe.test(text); emailRe.lastIndex = 0; return (getTitle(el) ? 3 : 0) + (hasDetail ? 3 : 0) + (ph ? 2 : 0) + (em ? 2 : 0) + (/\\b\\d{4}\\b/.test(text) ? 1 : 0); }; const selectors = ['article', '[data-testid*=result i]', '[data-testid*=listing i]', '[class*=Result], [class*=result]', 'li']; let cards = []; for (const sel of selectors) { const arr = qsa(sel).filter(el => !el.closest('#uscraper-localch-results') && score(el) >= 4); if (arr.length) { cards = arr; break; } } cards = cards.filter((el, i, arr) => !arr.some((other, j) => i !== j && other.contains(el) && score(other) >= score(el))).slice(0, 50); const sleep = ms => new Promise(r => setTimeout(r, ms)); const safeRevealContacts = async () => { const revealRe = /t[eé]l[eé]phone|telephone|telefon|phone|num[eé]ro|nummer|afficher|anzeigen|show number|voir le num[eé]ro|contact/i; for (const card of cards) { const controls = qsa('button,[role=button],a[href=\"#\"],a[href^=\"javascript\"]', card).filter(el => revealRe.test(compact((el.innerText || el.textContent || '') + ' ' + (el.getAttribute('aria-label') || '') + ' ' + (el.getAttribute('title') || '')))).slice(0, 4); for (const c of controls) { try { c.scrollIntoView({ block: 'center' }); c.click(); await sleep(120); } catch(e) {} } } await sleep(1800); }; const jsonValue = (html, keys) => { const decoded = decodeEscapes(html); for (const k of keys) { const r1 = new RegExp('\"' + k + '\"\\\\s*:\\\\s*\"([^\"\\\\\\\\]*(?:\\\\\\\\.[^\"\\\\\\\\]*)*)\"', 'i'); const m = decoded.match(r1); if (m) return decodeEscapes(m[1]); } return ''; }; const outgoingUrl = raw => { try { const u = new URL(raw, location.href); if (u.hostname.endsWith('local.ch')) { for (const p of ['url', 'u', 'target', 'redirect', 'to']) { const val = u.searchParams.get(p); if (val && /^https?:\\/\\//i.test(val)) return new URL(val).href; } return ''; } return u.href; } catch(e) { return ''; } }; const parseExternalFromHtml = html => { const decoded = decodeEscapes(html); const hrefs = Array.from(decoded.matchAll(/href=[\"']([^\"']+)[\"']/ig)).map(m => m[1]); const candidate = hrefs.map(outgoingUrl).find(h => h && !/local\\.ch|google\\.|facebook\\.|instagram\\.|twitter\\.|linkedin\\.|schema\\.org/i.test(h)); return candidate ? candidate.replace(/^https?:\\/\\//i, '').replace(/\\/$/, '') : ''; }; const parseOne = async el => { const title = getTitle(el); if (!title) return null; const href = detailHref(el); let detailDoc = null; let detailHtml = ''; if (href) { try { const res = await fetch(href, { credentials: 'include' }); detailHtml = decodeEscapes(await res.text()); detailDoc = new DOMParser().parseFromString(detailHtml, 'text/html'); } catch(e) {} } const docs = [el].concat(detailDoc ? [detailDoc] : []); const rawText = docs.map(d => d && d.body ? d.body.innerText : textOf(d)).join('\\n'); const combined = decodeEscapes(rawText + '\\n' + detailHtml.replace(/<[^>]+>/g, ' ')); const lines = combined.split(/\\n|\\r/).map(norm).filter(Boolean); const ratingNode = el.querySelector('[aria-label*=star i],[aria-label*=étoile i],[class*=rating i],[data-testid*=rating i]'); const ratingText = compact((ratingNode ? (ratingNode.getAttribute('aria-label') || '') + ' ' + textOf(ratingNode) : '') + ' ' + textOf(el)); const ratingMatch = ratingText.match(/\\b([1-5][,.][0-9])\\b/); const rating = ratingMatch ? ratingMatch[1].replace(',', '.') : ''; const addrNode = el.querySelector('address') || (detailDoc ? detailDoc.querySelector('address') : null); let address = textOf(addrNode); if (!address) { const street = jsonValue(detailHtml, ['streetAddress']); const zip = jsonValue(detailHtml, ['postalCode']); const city = jsonValue(detailHtml, ['addressLocality']); address = compact([street, [zip, city].filter(Boolean).join(' ')].filter(Boolean).join(', ')); } if (!address) address = lines.find(l => /\\b\\d{4}\\b/.test(l) && !phoneRe.test(l) && !emailRe.test(l) && l.toLowerCase() !== title.toLowerCase()) || ''; const telLinks = docs.flatMap(d => qsa('a[href^=tel]', d).map(a => cleanPhone(a.getAttribute('href')))); const htmlTels = Array.from(detailHtml.matchAll(/tel:([^\"'<>\\s]+)/ig)).map(m => cleanPhone(m[1])); const jsonTel = cleanPhone(jsonValue(detailHtml, ['telephone', 'phone'])); const textPhones = Array.from(combined.matchAll(phoneRe)).map(m => cleanPhone(m[0])); const phones = unique(telLinks.concat(htmlTels, jsonTel ? [jsonTel] : [], textPhones)); const labeledPhone = labels => { const labelRe = new RegExp(labels, 'i'); const line = lines.find(l => labelRe.test(l) && phoneRe.test(l)); if (!line) return ''; const m = line.match(phoneRe); return m ? cleanPhone(m[0]) : ''; }; const faxJson = cleanPhone(jsonValue(detailHtml, ['faxNumber', 'fax'])); const fax = faxJson || labeledPhone('fax|facsimile'); let mobile = labeledPhone('mobile|portable|natel|cellulaire'); if (!mobile) mobile = phones.find(p => /^07/.test(p) || /^\\+417/.test(p)) || ''; const telephone = phones.find(p => p !== fax && p !== mobile) || phones[0] || ''; const mailLink = docs.flatMap(d => qsa('a[href^=mailto]', d))[0]; const htmlMail = (detailHtml.match(/mailto:([^\"'<>?]+)/i) || [])[1] || ''; const jsonEmail = jsonValue(detailHtml, ['email']); const emailMatch = combined.match(emailRe); const email = mailLink ? mailLink.getAttribute('href').replace(/^mailto:/i, '').split('?')[0] : (htmlMail || jsonEmail || (emailMatch ? emailMatch[0] : '')); const externalLinks = docs.flatMap(d => qsa('a[href]', d)).map(a => outgoingUrl(a.getAttribute('href'))).filter(h => h && !/^mailto:|^tel:/i.test(h) && !/local\\.ch|google\\.|facebook\\.|instagram\\.|twitter\\.|linkedin\\./i.test(h)); const jsonUrl = jsonValue(detailHtml, ['url', 'sameAs']); const siteRaw = externalLinks[0] || (jsonUrl && !/local\\.ch/i.test(jsonUrl) ? jsonUrl : '') || parseExternalFromHtml(detailHtml); const site = siteRaw ? siteRaw.replace(/^https?:\\/\\//i, '').replace(/\\/$/, '') : ''; return { title, rating, address, telephone, fax, mobile, email, site }; }; const findNext = () => { const clickCount = parseInt(sessionStorage.getItem('uscraperLocalchPageClicks') || '0', 10); if (clickCount >= 1) return null; const candidates = qsa('a,button,[role=button]').filter(el => !el.disabled && el.getAttribute('aria-disabled') !== 'true' && !/disabled/i.test(el.className || '')); return candidates.find(el => { const txt = compact((el.innerText || el.textContent || '') + ' ' + (el.getAttribute('aria-label') || '') + ' ' + (el.getAttribute('title') || '') + ' ' + (el.getAttribute('rel') || '')).toLowerCase(); const href = el.href || el.getAttribute('href') || ''; let safeHref = false; try { const u = new URL(href, location.href); safeHref = u.hostname.endsWith('local.ch') && u.href !== location.href && (/\\/s\\//.test(u.pathname) || /[?&](page|p|offset|start|pos)=\\d+/i.test(u.search) || /\\/page\\/\\d+/i.test(u.pathname)); } catch(e) {} const exactNext = /(^|\\s)(next|suivant|weiter)(\\s|$)/i.test(txt) && (safeHref || /next/i.test(el.getAttribute('rel') || '')); const loadMore = /afficher plus de r[eé]sultats|plus de r[eé]sultats|show more results|load more|mehr anzeigen|weitere anzeigen|mostra altri risultati/i.test(txt); return exactNext || loadMore; }); }; safeRevealContacts().then(() => Promise.all(cards.map(parseOne))).then(rows => { rows.filter(Boolean).forEach(r => { const key = (r.title + '|' + r.address).toLowerCase(); if (window.__uscraperLocalchSeen.has(key)) return; window.__uscraperLocalchSeen.add(key); const row = document.createElement('div'); row.className = 'uscraper-localch-row'; row.setAttribute('data-title', r.title || ''); row.setAttribute('data-rating', r.rating || ''); row.setAttribute('data-address', r.address || ''); row.setAttribute('data-telephone', r.telephone || ''); row.setAttribute('data-fax', r.fax || ''); row.setAttribute('data-mobile', r.mobile || ''); row.setAttribute('data-email', r.email || ''); row.setAttribute('data-website', r.site || ''); root.appendChild(row); }); const nextReal = root.querySelector('.uscraper-localch-row') ? findNext() : null; if (nextReal) { const btn = document.createElement('button'); btn.id = 'uscraper-localch-next-button'; btn.textContent = 'UScraper Next / Load More'; btn.style.position = 'fixed'; btn.style.right = '20px'; btn.style.bottom = '20px'; btn.style.zIndex = '2147483647'; btn.style.padding = '10px'; btn.style.background = '#ff832b'; btn.style.color = '#000'; btn.onclick = () => { const n = parseInt(sessionStorage.getItem('uscraperLocalchPageClicks') || '0', 10); sessionStorage.setItem('uscraperLocalchPageClicks', String(n + 1)); const href = nextReal.href || nextReal.getAttribute('href'); if (href && !String(href).startsWith('#')) location.href = new URL(href, location.href).href; else nextReal.click(); }; document.body.appendChild(btn); } }).catch(err => console.error('UScraper local.ch enrichment failed', err)).finally(markDone); })();",
        "waitForCompletion": true,
        "timeout": 90
      }
    },
    {
      "block_id": "wait-for-element-2",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 2280,
      "position_y": 240,
      "config": {
        "selector": "#uscraper-localch-done",
        "timeout": 90,
        "visible": false
      }
    },
    {
      "block_id": "element-exists-2",
      "block_type": "process",
      "title": "Element Exists",
      "description": "Check if element exists",
      "position_x": 2640,
      "position_y": 240,
      "config": {
        "selector": "#uscraper-localch-results .uscraper-localch-row"
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 3000,
      "position_y": 240,
      "config": {
        "rowSelector": "#uscraper-localch-results .uscraper-localch-row",
        "fileName": "local-lead-scraper-via-url.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "columns": [
          {
            "name": "titre",
            "selector": "",
            "attribute": "data-title"
          },
          {
            "name": "rating",
            "selector": "",
            "attribute": "data-rating"
          },
          {
            "name": "adresse",
            "selector": "",
            "attribute": "data-address"
          },
          {
            "name": "telephone",
            "selector": "",
            "attribute": "data-telephone"
          },
          {
            "name": "fax",
            "selector": "",
            "attribute": "data-fax"
          },
          {
            "name": "portable",
            "selector": "",
            "attribute": "data-mobile"
          },
          {
            "name": "email",
            "selector": "",
            "attribute": "data-email"
          },
          {
            "name": "site_web",
            "selector": "",
            "attribute": "data-website"
          }
        ]
      }
    },
    {
      "block_id": "element-exists-1",
      "block_type": "process",
      "title": "Element Exists",
      "description": "Check if element exists",
      "position_x": 3360,
      "position_y": 240,
      "config": {
        "selector": "#uscraper-localch-next-button"
      }
    },
    {
      "block_id": "end-1",
      "block_type": "output",
      "title": "End",
      "description": "Terminate execution flow",
      "position_x": 3360,
      "position_y": 600,
      "config": {}
    },
    {
      "block_id": "click-1",
      "block_type": "process",
      "title": "Click",
      "description": "Click on element",
      "position_x": 3720,
      "position_y": 600,
      "config": {
        "selector": "#uscraper-localch-next-button",
        "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": 4080,
      "position_y": 600,
      "config": {
        "timeout": 30
      }
    },
    {
      "block_id": "sleep-2",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 4440,
      "position_y": 600,
      "config": {
        "duration": 3
      }
    }
  ],
  "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": "inject-javascript-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "inject-javascript-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": "element-exists-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-exists-2",
      "from_connector_id": "true",
      "to_block_id": "structured-export-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-exists-2",
      "from_connector_id": "false",
      "to_block_id": "end-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": "false",
      "to_block_id": "end-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": "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-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-2",
      "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": 136,
      "width": 4640,
      "height": 656,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "sleep-1",
          "wait-for-element-1",
          "wait-for-element-2",
          "wait-for-page-load-2",
          "sleep-2"
        ]
      }
    },
    {
      "id": "group-interaction",
      "element_type": "group",
      "title": "Interaction",
      "color": "#a56eff",
      "position_x": 768,
      "position_y": 136,
      "width": 1400,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "inject-javascript-1",
          "inject-javascript-2"
        ]
      }
    },
    {
      "id": "group-pagination",
      "element_type": "group",
      "title": "Pagination Loop",
      "color": "#ff832b",
      "position_x": 2568,
      "position_y": 136,
      "width": 1400,
      "height": 656,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "element-exists-2",
          "element-exists-1",
          "click-1"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 2928,
      "position_y": 136,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "structured-export-1"
        ]
      }
    },
    {
      "id": "group-control",
      "element_type": "group",
      "title": "Control Flow",
      "color": "#8d8d8d",
      "position_x": 3288,
      "position_y": 496,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "end-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Scrapes local.ch listing/search pages such as https://www.local.ch/fr/s/restaurant?rid=6b1d51 and exports lead fields matching the Octoparse template: title, rating, address, telephone, fax, mobile/portable, email, and website. The workflow dismisses cookie prompts, clicks visible show-contact controls inside result cards where possible, fetches local.ch detail pages as a fallback, exports normalized rows to CSV, and uses a guarded pagination loop. If local.ch presents CAPTCHA, bot protection, or hides contact details behind anti-scraping controls, manual intervention may be required.",
      "color": "#f1c21b",
      "position_x": 80,
      "position_y": 20,
      "width": 480,
      "height": 160,
      "z_index": 22,
      "data": {}
    },
    {
      "id": "note-block-inject-javascript-1",
      "element_type": "note",
      "title": "Note: Inject JavaScript",
      "content": "Runs custom JavaScript in the page: `(() => { const norm = s => (s || '').replace(/\\s+/g, ' ').trim().toLowerCase(); Array.from(document....` 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-inject-javascript-2",
      "element_type": "note",
      "title": "Note: Inject JavaScript",
      "content": "Runs custom JavaScript in the page: `(() => { const norm = s => (s || '').replace(/\\s+/g, ' ').trim(); const compact = s => norm(s).repla...` Verify in browser if results are empty.",
      "color": "#ee5396",
      "position_x": 2120,
      "position_y": 220,
      "width": 340,
      "height": 140,
      "z_index": 22,
      "data": {
        "block_id": "inject-javascript-2"
      }
    },
    {
      "id": "note-block-element-exists-2",
      "element_type": "note",
      "title": "Note: Element Exists",
      "content": "Condition block: checks `#uscraper-localch-results .uscraper-localch-row`. True / False branches control which path runs next. Keep enough space between branches so both connector lines are visible.",
      "color": "#ee5396",
      "position_x": 2840,
      "position_y": 220,
      "width": 340,
      "height": 146,
      "z_index": 22,
      "data": {
        "block_id": "element-exists-2"
      }
    },
    {
      "id": "note-block-element-exists-1",
      "element_type": "note",
      "title": "Note: Element Exists",
      "content": "Condition block: checks `#uscraper-localch-next-button`. True / False branches control which path runs next. Keep enough space between branches so both connector lines are visible.",
      "color": "#ee5396",
      "position_x": 3560,
      "position_y": 220,
      "width": 340,
      "height": 140,
      "z_index": 22,
      "data": {
        "block_id": "element-exists-1"
      }
    },
    {
      "id": "note-block-click-1",
      "element_type": "note",
      "title": "Note: Click",
      "content": "Pagination click — add waits after this block; the page reloads asynchronously.",
      "color": "#ee5396",
      "position_x": 3920,
      "position_y": 580,
      "width": 316,
      "height": 106,
      "z_index": 22,
      "data": {
        "block_id": "click-1"
      }
    }
  ]
}