{
  "version": "1.0.0",
  "exported_at": "2026-06-02T23:59:00.000Z",
  "project": {
    "name": "Belles Demeures Biens en location Scraper",
    "description": "Extracts rental property information from Belles Demeures rental URLs, including property type, rooms, surface area, address, monthly rent, taxes, charges, agency, detail URL, photo URL, description, and phone number. The template accepts multiple input URLs in navigate.urls[]. It supports both detail pages and cases where expired detail URLs redirect to Belles Demeures search/listing pages. JavaScript normalizes property cards/detail data into hidden rows, follows next-page pagination when present, and appends all extracted rows. Agency and address extraction includes filters to avoid mortgage-simulation labels and type-only addresses. Phone numbers are best-effort because Belles Demeures/SeLoger may hide them behind contact widgets or anti-bot checks.",
    "color": "bg-[#4589ff]",
    "template_id": "ai-generated"
  },
  "blocks": [
    {
      "block_id": "navigate-1",
      "block_type": "process",
      "title": "Navigate",
      "description": "Go to each Belles Demeures rental detail or listing URL",
      "position_x": 120,
      "position_y": 240,
      "config": {
        "urls": [
          "https://www.bellesdemeures.com/annonces/location/tt-1-tb-1-pl-48173/220663323/#?m=search_to_detail",
          "https://www.bellesdemeures.com/annonces/location/tt-1-tb-2-pl-46128/219608923/#?m=search_to_detail",
          "https://www.bellesdemeures.com/annonces/location/tt-1-tb-1-pl-48213/220564125/#?m=search_to_detail",
          "https://www.bellesdemeures.com/annonces/location/tt-1-tb-2-pl-39180/219422007/#?m=search_to_detail",
          "https://www.bellesdemeures.com/annonces/location/tt-1-tb-2-pl-48256/220534785/#?m=search_to_detail"
        ],
        "color": "bg-[#4589ff]"
      }
    },
    {
      "block_id": "wait-for-page-load-1",
      "block_type": "process",
      "title": "Wait for Page Load",
      "description": "Wait for the page document to finish loading",
      "position_x": 480,
      "position_y": 240,
      "config": {
        "timeout": 45
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Allow client-side scripts and listing data to hydrate",
      "position_x": 840,
      "position_y": 240,
      "config": {
        "duration": 3
      }
    },
    {
      "block_id": "inject-javascript-1",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Normalize Belles Demeures property data into hidden rows and prepare next-page control",
      "position_x": 1200,
      "position_y": 240,
      "config": {
        "jsCode": "(() => { const F=['type_de_logement','pieces','espace','adresse','loyer_mensuel','taxes','charges','agences_immobilieres','url_du_page_detaille','photo_url','description','telephone']; const clean=v=>String(v==null?'':v).replace(/\\s+/g,' ').trim(); const norm=v=>clean(v).toLowerCase().normalize('NFD').replace(/[\\u0300-\\u036f]/g,''); const abs=u=>{u=clean(u); if(!u)return ''; try{return new URL(u,location.href).href}catch(e){return u}}; const titleCase=s=>clean(s.replace(/-/g,' ')).replace(/\\b\\w/g,c=>c.toUpperCase()); const isType=s=>/^(appartement|duplex|villa|maison|maison\\s*\\/\\s*villa|propriété|propriete|château|chateau|hôtel particulier|hotel particulier|loft|penthouse)$/i.test(clean(s)); const badAgency=/message|envoy|cookie|favori|alerte|voir|numero|numéro|telephone|téléphone|contactez|login|connexion|newsletter|belles demeures|simulez|simulation|prêt|pret|crédit|credit|financement|calcul/i; const badDesc=/consultez nos annonces|louez un hôtel particulier|louez un hotel particulier|notre agence a vu le jour|plus qu.une signature immobiliere|plus qu.une signature immobilière|belles demeures - immobilier|cookies/i; const clickEl=el=>{try{el.scrollIntoView({block:'center'});el.click();return true}catch(e){return false}}; ['#onetrust-accept-btn-handler','button[id*=accept i]','button[class*=accept i]','[data-testid*=accept i]','[aria-label*=Accept i]','[aria-label*=Accepter i]'].some(s=>{const e=document.querySelector(s);return e?clickEl(e):false}); Array.from(document.querySelectorAll('button,a,[role=button]')).filter(el=>{const t=norm(el.innerText||el.textContent||el.getAttribute('aria-label')||el.getAttribute('title')||''); return /(voir|afficher|reveler|révéler).{0,30}(numero|numéro|telephone|téléphone)|appeler l.agence|telephone de l.agence|téléphone de l.agence/.test(t)&&!/(message|email|mail|formulaire|contacter par message)/.test(t)}).slice(0,3).forEach(clickEl); const old=document.querySelector('#uscraper-bd-rows'); if(old)old.remove(); const oldNext=document.querySelector('#us-next-page'); if(oldNext)oldNext.remove(); const priceOf=t=>{const m=clean(t).match(/(\\d[\\d\\s]*(?:[,.]\\d{1,2})?)\\s*(?:€|EUR|euros?)/i); return m?m[1].replace(/\\s+/g,' ').trim()+' €':''}; const surfaceOf=t=>{const m=clean(t).match(/\\b(\\d[\\d\\s]*(?:[,.]\\d+)?)\\s*m(?:²|2)\\b/i); return m?m[1].replace(/\\s+/g,' ').trim()+' m²':''}; const piecesOf=t=>{const m=clean(t).match(/\\b(\\d{1,3})\\s*pi[eè]ces?\\b/i)||clean(t).match(/\\b(\\d{1,3})\\s*p\\b/i); return m?m[1]:''}; const typeOf=t=>{const m=clean(t).match(/\\b(Appartement|Villa|Maison\\s*\\/\\s*Villa|Maison|Propriété|Propriete|Château|Chateau|Hôtel particulier|Hotel particulier|Loft|Duplex|Penthouse)\\b/i); return m?clean(m[1]):''}; const phoneOf=t=>{const m=clean(t).match(/(?:\\+33|0033|0)\\s*[1-9](?:[\\s().-]*\\d{2}){4}/); return m?m[0].replace(/\\s+/g,''):''}; const addressFromUrl=u=>{u=abs(u); if(!/bellesdemeures\\.com/.test(u))return ''; let p=''; try{p=decodeURIComponent(new URL(u).pathname)}catch(e){return ''} const parts=p.split('/').filter(Boolean); const typeIndex=parts.findIndex(x=>/(appartement|villa|maison|hotel|hôtel|propriete|propriété|chateau|château|loft|duplex|penthouse)/i.test(x)); if(typeIndex<0)return ''; let loc=parts.slice(0,typeIndex).filter(x=>!/(location|vente|france|ile-de-france|provence-alpes|cote-d-azur|côte-d-azur|departement|annonces|tt-|tb-|pl-|opt-)/i.test(x)); loc=loc.slice(-2); if(!loc.length)return ''; return loc.reverse().map(titleCase).join(', ') }; const likelyAddress=(lines,u,type)=>{const fromUrl=addressFromUrl(u); if(fromUrl&&!isType(fromUrl))return fromUrl; const cleanLines=lines.map(clean).filter(Boolean); const strong=cleanLines.find(l=>/(paris\\s*\\d|paris\\s*\\d+ème|paris\\s*\\d+eme|saint-tropez|cannes|nice|neuilly|boulogne|bordeaux|monaco|lyon|marseille|odéon|odeon|porte dauphine|gare|trudaine|maubeuge|zone ouest)/i.test(l)&&l.length<160&&!isType(l)&&!/belles demeures|immobilier de luxe|simulez|prêt|pret/i.test(l)); if(strong)return strong; const city=cleanLines.find(l=>/(paris|saint-|cannes|tropez|nice|lyon|marseille|monaco|ème|eme)/i.test(l)&&l.length<140&&!isType(l)&&!/belles demeures|immobilier de luxe|simulez|prêt|pret/i.test(l)); return city||''}; const imageFrom=root=>{const img=root.querySelector('img'); if(!img)return ''; const src=img.currentSrc||img.src||img.getAttribute('data-src')||img.getAttribute('data-original')||''; return abs(src)}; const rootFor=a=>{let best=a; let p=a; for(let i=0;i<7&&p;i++,p=p.parentElement){const t=clean(p.innerText||p.textContent); if(t.length>40&&t.length<5000&&/(€|m²|m2|pi[eè]ces?)/i.test(t))best=p} return best}; const agencyFromLines=lines=>{const candidates=lines.map(clean).filter(l=>l.length>2&&l.length<120&&!badAgency.test(l)&&!isType(l)&&!/(€|m²|m2|pi[eè]ces?|appartement|villa|maison|duplex|location|vente|paris|cannes|saint-|tropez|charges|honoraires|réf|ref)/i.test(l)); return candidates.find(l=>/[A-Z]{2,}/.test(l)||/Immobilier|Realty|Estate|Barnes|Agence|Properties|Luxury|Conseil|Patrimoine|Rentals|Sotheby|Coldwell|Varenne|Daniel Féau|Michaël Zingraf/i.test(l))||''}; const rowFromCard=a=>{const u=abs(a.getAttribute('href')||a.href||''); const root=rootFor(a); const raw=root.innerText||root.textContent||a.innerText||''; const lines=raw.split(/\\n+/).map(clean).filter(Boolean); const text=clean(raw); const typ=typeOf(text)||typeOf(u); const addr=likelyAddress(lines,u,typ); return {type_de_logement:typ,pieces:piecesOf(text),espace:surfaceOf(text),adresse:isType(addr)||norm(addr)===norm(typ)?'':addr,loyer_mensuel:priceOf(text),taxes:(text.match(/\\b(TTC|HT)\\b/i)?.[1]||'').toUpperCase(),charges:(text.match(/\\b(CC|HC|CH)\\b/i)?.[1]||text.match(/charges\\s+(comprises|hors charges)/i)?.[1]||'').toUpperCase(),agences_immobilieres:agencyFromLines(lines),url_du_page_detaille:u,photo_url:imageFrom(root),description:'',telephone:phoneOf(text)} }; const roots=[]; const addJson=t=>{t=(t||'').trim(); if(!/^[\\[{]/.test(t))return; try{roots.push(JSON.parse(t))}catch(e){}}; const nd=document.querySelector('#__NEXT_DATA__'); if(nd)addJson(nd.textContent); document.querySelectorAll('script[type=\"application/ld+json\"],script[type=\"application/json\"]').forEach(s=>addJson(s.textContent)); const objs=[]; const seenObj=new Set(); const walk=v=>{if(!v||typeof v!=='object'||seenObj.has(v))return; seenObj.add(v); if(!Array.isArray(v))objs.push(v); Object.values(v).forEach(walk)}; roots.forEach(walk); const first=o=>{if(o==null)return ''; if(typeof o==='string'||typeof o==='number')return clean(o); if(Array.isArray(o)){for(const x of o){const r=first(x); if(r)return r}} if(typeof o==='object'){for(const k of ['url','href','src','name','label','value','text','description']){const r=first(o[k]); if(r)return r}} return ''}; const vals=(o,re,out=[])=>{if(!o||typeof o!=='object')return out; if(Array.isArray(o)){o.forEach(x=>vals(x,re,out));return out} for(const [k,v] of Object.entries(o)){if(re.test(k))out.push(v); if(v&&typeof v==='object')vals(v,re,out)} return out}; const jsonText=o=>{try{return JSON.stringify(o)}catch(e){return ''}}; const rowFromObj=o=>{const jt=jsonText(o); const url=(vals(o,/url|href|permalink|detail|link/i).map(first).map(abs).find(x=>/bellesdemeures\\.com\\/.+(annonces|location\\/france)/.test(x)&&!/recherche/.test(x))||''); const img=(vals(o,/photo|image|visuel|visual|picture|src/i).map(first).map(abs).find(x=>/seloger|visuels|\\.(jpg|jpeg|png|webp)(\\?|$)/i.test(x))||''); const agency=(vals(o,/agency|agence|professional|advertiser|seller|company|brand|commercialName|name/i).map(first).find(x=>x.length>2&&x.length<140&&!badAgency.test(x)&&!isType(x))||''); const desc=(vals(o,/description|descriptif|body|text/i).map(first).filter(x=>x.length>80&&!badDesc.test(norm(x))).sort((a,b)=>b.length-a.length)[0]||''); const typ=typeOf(jt); const addr=addressFromUrl(url); return {type_de_logement:typ,pieces:piecesOf(jt),espace:surfaceOf(jt),adresse:isType(addr)||norm(addr)===norm(typ)?'':addr,loyer_mensuel:priceOf(jt),taxes:(jt.match(/\\b(TTC|HT)\\b/i)?.[1]||'').toUpperCase(),charges:(jt.match(/\\b(CC|HC|CH)\\b/i)?.[1]||'').toUpperCase(),agences_immobilieres:agency,url_du_page_detaille:url||location.href,photo_url:img,description:desc,telephone:phoneOf(jt)} }; let rows=[]; const anchors=Array.from(document.querySelectorAll('a[href]')).filter(a=>{const u=abs(a.getAttribute('href')||a.href||''); if(!/bellesdemeures\\.com/.test(u))return false; if(/recherche|#|javascript:|mailto:|tel:|jpg|jpeg|png|webp/i.test(u))return false; return /(annonces\\/location|\\/location\\/france\\/)/.test(u)}); anchors.forEach(a=>rows.push(rowFromCard(a))); if(!rows.length){const scored=objs.map(o=>({o,t:jsonText(o)})).filter(x=>/(€|EUR|loyer|prix|price|m²|m2|surface|annonces\\/location|\\/location\\/france|visuels)/i.test(x.t)).slice(0,80); scored.forEach(x=>rows.push(rowFromObj(x.o)));} if(!rows.length){const text=document.body?document.body.innerText||'':''; const lines=text.split(/\\n+/).map(clean).filter(Boolean); const typ=typeOf(text)||typeOf(document.title); const metaImg=document.querySelector('meta[property=\"og:image\"],meta[name=\"twitter:image\"]')?.content||''; const metaDesc=document.querySelector('meta[name=\"description\"],meta[property=\"og:description\"]')?.content||''; const addr=likelyAddress(lines,location.href,typ); rows.push({type_de_logement:typ,pieces:piecesOf(text),espace:surfaceOf(text),adresse:isType(addr)||norm(addr)===norm(typ)?'':addr,loyer_mensuel:priceOf(text),taxes:(text.match(/\\b(TTC|HT)\\b/i)?.[1]||'').toUpperCase(),charges:(text.match(/\\b(CC|HC|CH)\\b/i)?.[1]||'').toUpperCase(),agences_immobilieres:agencyFromLines(lines),url_du_page_detaille:location.href,photo_url:abs(metaImg),description:badDesc.test(norm(metaDesc))?'':clean(metaDesc),telephone:phoneOf(text)});} const seenRows=new Set(); rows=rows.map(r=>{if(/recherche/i.test(r.url_du_page_detaille))r.url_du_page_detaille=''; if(!r.adresse)r.adresse=addressFromUrl(r.url_du_page_detaille); if(isType(r.adresse)||norm(r.adresse)===norm(r.type_de_logement)||/belles demeures|immobilier de luxe|simulez|prêt|pret/i.test(r.adresse))r.adresse=''; if(badAgency.test(r.agences_immobilieres))r.agences_immobilieres=''; if(badDesc.test(norm(r.description)))r.description=''; F.forEach(f=>r[f]=clean(r[f]||'')); return r}).filter(r=>{if(!r.url_du_page_detaille&&/recherche/i.test(location.href))return false; const k=(r.url_du_page_detaille||location.href)+'|'+r.loyer_mensuel+'|'+r.espace+'|'+r.photo_url; if(seenRows.has(k))return false; seenRows.add(k); return r.loyer_mensuel||r.photo_url||r.type_de_logement||r.description}); const box=document.createElement('div'); box.id='uscraper-bd-rows'; box.style.display='none'; rows.forEach(r=>{const row=document.createElement('div'); row.setAttribute('data-us-row','property'); F.forEach(f=>{const s=document.createElement('span'); s.setAttribute('data-field',f); s.textContent=r[f]||''; row.appendChild(s)}); box.appendChild(row)}); document.body.appendChild(box); const next=Array.from(document.querySelectorAll('a,button')).find(el=>{const t=norm(el.innerText||el.textContent||el.getAttribute('aria-label')||el.getAttribute('title')||el.getAttribute('rel')||''); const disabled=el.disabled||el.getAttribute('aria-disabled')==='true'||/disabled|inactive/.test(el.className||''); return !disabled&&/(suivant|next|page suivante|›|»)/.test(t)}); if(next){const b=document.createElement('button'); b.id='us-next-page'; b.textContent='UScraper Next Page'; b.style.position='fixed'; b.style.left='10px'; b.style.top='10px'; b.style.zIndex='2147483647'; const href=next.getAttribute&&next.getAttribute('href'); b.onclick=()=>{if(href)location.href=abs(href);else next.click()}; document.body.appendChild(b)} return rows.length; })();",
        "waitForCompletion": true,
        "timeout": 15
      }
    },
    {
      "block_id": "sleep-2",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait after data preparation",
      "position_x": 1560,
      "position_y": 240,
      "config": {
        "duration": 1
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait for normalized property rows created by JavaScript",
      "position_x": 1920,
      "position_y": 240,
      "config": {
        "selector": "#uscraper-bd-rows [data-us-row=\"property\"]",
        "timeout": 30,
        "visible": false
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export normalized Belles Demeures property rows",
      "position_x": 2280,
      "position_y": 240,
      "config": {
        "rowSelector": "#uscraper-bd-rows [data-us-row=\"property\"]",
        "fileName": "belles_demeures_biens_en_location_scraper_v2.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "columns": [
          {
            "name": "type_de_logement",
            "selector": "[data-field=\"type_de_logement\"]",
            "attribute": "text"
          },
          {
            "name": "pieces",
            "selector": "[data-field=\"pieces\"]",
            "attribute": "text"
          },
          {
            "name": "espace",
            "selector": "[data-field=\"espace\"]",
            "attribute": "text"
          },
          {
            "name": "adresse",
            "selector": "[data-field=\"adresse\"]",
            "attribute": "text"
          },
          {
            "name": "loyer_mensuel",
            "selector": "[data-field=\"loyer_mensuel\"]",
            "attribute": "text"
          },
          {
            "name": "taxes",
            "selector": "[data-field=\"taxes\"]",
            "attribute": "text"
          },
          {
            "name": "charges",
            "selector": "[data-field=\"charges\"]",
            "attribute": "text"
          },
          {
            "name": "agences_immobilieres",
            "selector": "[data-field=\"agences_immobilieres\"]",
            "attribute": "text"
          },
          {
            "name": "url_du_page_detaille",
            "selector": "[data-field=\"url_du_page_detaille\"]",
            "attribute": "text"
          },
          {
            "name": "photo_url",
            "selector": "[data-field=\"photo_url\"]",
            "attribute": "text"
          },
          {
            "name": "description",
            "selector": "[data-field=\"description\"]",
            "attribute": "text"
          },
          {
            "name": "telephone",
            "selector": "[data-field=\"telephone\"]",
            "attribute": "text"
          }
        ]
      }
    },
    {
      "block_id": "element-exists-1",
      "block_type": "process",
      "title": "Element Exists",
      "description": "Check whether a next page control was detected",
      "position_x": 2640,
      "position_y": 240,
      "config": {
        "selector": "#us-next-page"
      }
    },
    {
      "block_id": "click-1",
      "block_type": "process",
      "title": "Click",
      "description": "Click the synthetic next-page control",
      "position_x": 2640,
      "position_y": 560,
      "config": {
        "selector": "#us-next-page",
        "timeout": 10
      }
    },
    {
      "block_id": "wait-for-page-load-2",
      "block_type": "process",
      "title": "Wait for Page Load",
      "description": "Wait after pagination click",
      "position_x": 3000,
      "position_y": 560,
      "config": {
        "timeout": 45
      }
    },
    {
      "block_id": "sleep-3",
      "block_type": "process",
      "title": "Sleep",
      "description": "Allow next listing page content to hydrate before rebuilding rows",
      "position_x": 3360,
      "position_y": 560,
      "config": {
        "duration": 2
      }
    },
    {
      "block_id": "loop-continue-1",
      "block_type": "process",
      "title": "Loop Continue",
      "description": "Advance to the next supplied input URL after all pages for the current URL are scraped",
      "position_x": 2640,
      "position_y": 880,
      "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": "sleep-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-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-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"
    },
    {
      "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": "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-3",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-3",
      "from_connector_id": "right",
      "to_block_id": "inject-javascript-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-exists-1",
      "from_connector_id": "false",
      "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": 3560,
      "height": 616,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "sleep-1",
          "sleep-2",
          "wait-for-element-1",
          "wait-for-page-load-2",
          "sleep-3"
        ]
      }
    },
    {
      "id": "group-interaction",
      "element_type": "group",
      "title": "Interaction",
      "color": "#a56eff",
      "position_x": 1128,
      "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": 2208,
      "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": 2568,
      "position_y": 136,
      "width": 380,
      "height": 936,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "element-exists-1",
          "click-1",
          "loop-continue-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Extracts rental property information from Belles Demeures rental URLs, including property type, rooms, surface area, address, monthly rent, taxes, charges, agency, detail URL, photo URL, description, and phone number. The template accepts multiple input URLs in navigate.urls[]. It supports both detail pages and cases where expired detail URLs redirect to Belles Demeures search/listing pages. JavaScript normalizes property cards/detail data into hidden rows, follows next-page pagination when present, and appends all extracted rows. Agency and address extraction includes filters to avoid mortgage-simulation labels and type-only addresses. Phone numbers are best-effort because Belles Demeures/SeLoger may hide them behind contact widgets or anti-bot checks.",
      "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 F=['type_de_logement','pieces','espace','adresse','loyer_mensuel','taxes','charges','...` Verify in browser if results are empty.",
      "color": "#ee5396",
      "position_x": 1400,
      "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": "Extracts rows matching `#uscraper-bd-rows [data-us-row=\"property\"]`. Confirm row count > 0 before running at scale.",
      "color": "#ee5396",
      "position_x": 2480,
      "position_y": 220,
      "width": 340,
      "height": 118,
      "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 `#us-next-page`. 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": 134,
      "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": 2840,
      "position_y": 540,
      "width": 316,
      "height": 106,
      "z_index": 22,
      "data": {
        "block_id": "click-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": 2840,
      "position_y": 860,
      "width": 340,
      "height": 123,
      "z_index": 22,
      "data": {
        "block_id": "loop-continue-1"
      }
    }
  ]
}