{
  "version": "1.0.0",
  "exported_at": "2026-06-02T15:45:00.000Z",
  "project": {
    "name": "Booking Details Scraper for Spanish",
    "description": "Extracts Booking.com hotel detail data in Spanish from hotel detail page URLs, equivalent to the Octoparse template's 'enter URL of details pages' workflow. The starter list contains one confirmed working Booking.com hotel detail URL; replace or extend navigate.urls with additional Booking hotel detail URLs. The flow uses a multi-URL loop and appends one CSV row per detail page. Selectors are best-effort Booking.com selectors plus JSON-LD fallbacks. Booking may block automation, show CAPTCHA/consent modals, or change layout.",
    "color": "bg-[#4589ff]",
    "template_id": "ai-generated"
  },
  "blocks": [
    {
      "block_id": "set-window-size-1",
      "block_type": "process",
      "title": "Set Window Size",
      "description": "Set browser viewport dimensions",
      "position_x": 120,
      "position_y": 300,
      "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": 300,
      "config": {
        "urls": [
          "https://www.booking.com/hotel/es/hostal-noria.es.html?lang=es"
        ],
        "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": 300,
      "config": {
        "timeout": 40
      }
    },
    {
      "block_id": "inject-javascript-1",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Run custom JavaScript on the page",
      "position_x": 1200,
      "position_y": 300,
      "config": {
        "jsCode": "(()=>{const labels=['Aceptar','Accept','Aceptar todo','Accept all','Estoy de acuerdo','I agree','OK']; const buttons=[...document.querySelectorAll('button')]; const consent=buttons.find(b=>labels.some(t=>(b.innerText||b.textContent||'').trim().toLowerCase()===t.toLowerCase())); if(consent) consent.click(); setTimeout(()=>{[...document.querySelectorAll('button,a')].filter(el=>/(Leer más|Ver más|Mostrar más|Read more|Show more|See more)/i.test((el.innerText||el.textContent||'').trim())).slice(0,10).forEach(el=>{try{el.click();}catch(e){}});},500); return true;})()",
        "waitForCompletion": true,
        "timeout": 10
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 1560,
      "position_y": 300,
      "config": {
        "duration": 2
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 1920,
      "position_y": 300,
      "config": {
        "selector": "body",
        "timeout": 40,
        "visible": true
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 2280,
      "position_y": 300,
      "config": {
        "rowSelector": "body",
        "fileName": "booking_hotel_detalles_scraper.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "columns": [
          {
            "name": "hotel",
            "selector": "(()=>{function ld(){for(const s of document.querySelectorAll('script[type=\"application/ld+json\"]')){try{const j=JSON.parse(s.textContent); const a=Array.isArray(j)?j:[j]; const h=a.find(x=>x&&/(Hotel|LodgingBusiness|Accommodation)/i.test(String(x['@type']||''))); if(h)return h;}catch(e){}}return null;} const j=ld(); const el=ROW.querySelector('[data-testid=title], h2.pp-header__title, h2'); return (j?.name||el?.textContent||document.title||'').replace(/\\s+/g,' ').trim();})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "ubicacion",
            "selector": "(()=>{function ld(){for(const s of document.querySelectorAll('script[type=\"application/ld+json\"]')){try{const j=JSON.parse(s.textContent); const a=Array.isArray(j)?j:[j]; const h=a.find(x=>x&&/(Hotel|LodgingBusiness|Accommodation)/i.test(String(x['@type']||''))); if(h)return h;}catch(e){}}return null;} const j=ld(); const a=j?.address||{}; const parts=[a.streetAddress,a.postalCode,a.addressLocality,a.addressRegion,a.addressCountry?.name||a.addressCountry].filter(Boolean); if(parts.length)return parts.join(', '); const el=ROW.querySelector('[data-testid=address], .hp_address_subtitle, #showMap2 span'); return (el?.textContent||'').replace(/\\s+/g,' ').trim();})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "calificacion",
            "selector": "(()=>{function ld(){for(const s of document.querySelectorAll('script[type=\"application/ld+json\"]')){try{const j=JSON.parse(s.textContent); const a=Array.isArray(j)?j:[j]; const h=a.find(x=>x&&/(Hotel|LodgingBusiness|Accommodation)/i.test(String(x['@type']||''))); if(h)return h;}catch(e){}}return null;} const j=ld(); if(j?.aggregateRating?.ratingValue)return String(j.aggregateRating.ratingValue); const scope=ROW.querySelector('[data-testid=review-score-right-component], [data-testid=review-score-component]'); const txt=(scope?.textContent||'').replace(/\\s+/g,' '); const m=txt.match(/(\\d+[,.]\\d+)/); return m?m[1]:'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "etiqueta",
            "selector": "(()=>{const el=ROW.querySelector('[data-testid=property-type-badge], [data-testid=property-type], .hp__hotel-type-badge'); return (el?.textContent||'').replace(/\\s+/g,' ').trim();})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "descripcion",
            "selector": "(()=>{function ld(){for(const s of document.querySelectorAll('script[type=\"application/ld+json\"]')){try{const j=JSON.parse(s.textContent); const a=Array.isArray(j)?j:[j]; const h=a.find(x=>x&&/(Hotel|LodgingBusiness|Accommodation)/i.test(String(x['@type']||''))); if(h)return h;}catch(e){}}return null;} const j=ld(); if(j?.description)return String(j.description).replace(/\\s+/g,' ').trim(); const el=ROW.querySelector('[data-testid=property-description], #property_description_content, .hp_desc_main_content'); return (el?.innerText||el?.textContent||'').replace(/\\s+/g,' ').trim();})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "servicios_populares",
            "selector": "(()=>{const root=ROW.querySelector('[data-testid=property-most-popular-facilities-wrapper], .hp-facilities, #hp_facilities_box'); if(!root)return ''; return [...root.querySelectorAll('li, span, div')].map(e=>(e.textContent||'').replace(/\\s+/g,' ').trim()).filter((v,i,a)=>v&&v.length<90&&a.indexOf(v)===i&&!/Servicios más populares/i.test(v)).join(' | ');})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "categoria_calificar",
            "selector": "(()=>{const roots=[...ROW.querySelectorAll('[data-testid=review-subscore], .review_subscore, .c-score-bar')]; return roots.map(e=>(e.innerText||e.textContent||'').replace(/\\s+/g,' ').trim()).filter(Boolean).join(' | ');})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "que_hay_cerca",
            "selector": "(()=>{function section(rx){const heads=[...ROW.querySelectorAll('h2,h3,h4')]; const h=heads.find(e=>rx.test((e.textContent||'').trim())); if(!h)return ''; const ht=(h.textContent||'').replace(/\\s+/g,' ').trim(); let p=h.parentElement; for(let i=0;i<6&&p;i++,p=p.parentElement){const txt=(p.innerText||p.textContent||'').replace(/\\s+/g,' ').trim(); if(txt&&txt.length>ht.length+20&&txt.length<4000)return txt;} return ht;} return section(/Qué hay cerca|What's nearby/i);})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "restaurantes_y_cafeterias",
            "selector": "(()=>{function section(rx){const heads=[...ROW.querySelectorAll('h2,h3,h4')]; const h=heads.find(e=>rx.test((e.textContent||'').trim())); if(!h)return ''; const ht=(h.textContent||'').replace(/\\s+/g,' ').trim(); let p=h.parentElement; for(let i=0;i<6&&p;i++,p=p.parentElement){const txt=(p.innerText||p.textContent||'').replace(/\\s+/g,' ').trim(); if(txt&&txt.length>ht.length+20&&txt.length<4000)return txt;} return ht;} return section(/Restaurantes|cafeterías|cafeterias|Restaurants|cafes|cafés/i);})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "atracciones_turisticas_destacadas",
            "selector": "(()=>{function section(rx){const heads=[...ROW.querySelectorAll('h2,h3,h4')]; const h=heads.find(e=>rx.test((e.textContent||'').trim())); if(!h)return ''; const ht=(h.textContent||'').replace(/\\s+/g,' ').trim(); let p=h.parentElement; for(let i=0;i<6&&p;i++,p=p.parentElement){const txt=(p.innerText||p.textContent||'').replace(/\\s+/g,' ').trim(); if(txt&&txt.length>ht.length+20&&txt.length<4000)return txt;} return ht;} return section(/Atracciones turísticas|Atracciones turisticas|Top attractions|Lugares de interés|Attractions/i);})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "playas_la_zona",
            "selector": "(()=>{function section(rx){const heads=[...ROW.querySelectorAll('h2,h3,h4')]; const h=heads.find(e=>rx.test((e.textContent||'').trim())); if(!h)return ''; const ht=(h.textContent||'').replace(/\\s+/g,' ').trim(); let p=h.parentElement; for(let i=0;i<6&&p;i++,p=p.parentElement){const txt=(p.innerText||p.textContent||'').replace(/\\s+/g,' ').trim(); if(txt&&txt.length>ht.length+20&&txt.length<4000)return txt;} return ht;} return section(/Playas|Beaches/i);})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "transporte_publico",
            "selector": "(()=>{function section(rx){const heads=[...ROW.querySelectorAll('h2,h3,h4')]; const h=heads.find(e=>rx.test((e.textContent||'').trim())); if(!h)return ''; const ht=(h.textContent||'').replace(/\\s+/g,' ').trim(); let p=h.parentElement; for(let i=0;i<6&&p;i++,p=p.parentElement){const txt=(p.innerText||p.textContent||'').replace(/\\s+/g,' ').trim(); if(txt&&txt.length>ht.length+20&&txt.length<4000)return txt;} return ht;} return section(/Transporte público|Transporte publico|Public transit|Public transport/i);})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "aeropuertos_mas_cercanos",
            "selector": "(()=>{function section(rx){const heads=[...ROW.querySelectorAll('h2,h3,h4')]; const h=heads.find(e=>rx.test((e.textContent||'').trim())); if(!h)return ''; const ht=(h.textContent||'').replace(/\\s+/g,' ').trim(); let p=h.parentElement; for(let i=0;i<6&&p;i++,p=p.parentElement){const txt=(p.innerText||p.textContent||'').replace(/\\s+/g,' ').trim(); if(txt&&txt.length>ht.length+20&&txt.length<4000)return txt;} return ht;} return section(/Aeropuertos|Closest airports|Aeropuertos más cercanos|Aeropuertos mas cercanos/i);})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "imagen",
            "selector": "(()=>{function ldi(){for(const s of document.querySelectorAll('script[type=\"application/ld+json\"]')){try{const j=JSON.parse(s.textContent); const a=Array.isArray(j)?j:[j]; const h=a.find(x=>x&&x.image); if(h){const im=Array.isArray(h.image)?h.image:[h.image]; return im.filter(Boolean);}}catch(e){}}return [];} const dom=[...ROW.querySelectorAll('img')].map(img=>img.currentSrc||img.src||img.getAttribute('data-src')||img.getAttribute('data-lazy-src')||''); const imgs=[...ldi(),...dom].filter(src=>/cf\\.bstatic\\.com\\/xdata\\/images\\/hotel|bstatic\\.com\\/xdata\\/images\\/hotel/.test(src)); return [...new Set(imgs)][0]||'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "imagen2",
            "selector": "(()=>{function ldi(){for(const s of document.querySelectorAll('script[type=\"application/ld+json\"]')){try{const j=JSON.parse(s.textContent); const a=Array.isArray(j)?j:[j]; const h=a.find(x=>x&&x.image); if(h){const im=Array.isArray(h.image)?h.image:[h.image]; return im.filter(Boolean);}}catch(e){}}return [];} const dom=[...ROW.querySelectorAll('img')].map(img=>img.currentSrc||img.src||img.getAttribute('data-src')||img.getAttribute('data-lazy-src')||''); const imgs=[...ldi(),...dom].filter(src=>/cf\\.bstatic\\.com\\/xdata\\/images\\/hotel|bstatic\\.com\\/xdata\\/images\\/hotel/.test(src)); return [...new Set(imgs)][1]||'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "imagen3",
            "selector": "(()=>{function ldi(){for(const s of document.querySelectorAll('script[type=\"application/ld+json\"]')){try{const j=JSON.parse(s.textContent); const a=Array.isArray(j)?j:[j]; const h=a.find(x=>x&&x.image); if(h){const im=Array.isArray(h.image)?h.image:[h.image]; return im.filter(Boolean);}}catch(e){}}return [];} const dom=[...ROW.querySelectorAll('img')].map(img=>img.currentSrc||img.src||img.getAttribute('data-src')||img.getAttribute('data-lazy-src')||''); const imgs=[...ldi(),...dom].filter(src=>/cf\\.bstatic\\.com\\/xdata\\/images\\/hotel|bstatic\\.com\\/xdata\\/images\\/hotel/.test(src)); return [...new Set(imgs)][2]||'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "imagen4",
            "selector": "(()=>{function ldi(){for(const s of document.querySelectorAll('script[type=\"application/ld+json\"]')){try{const j=JSON.parse(s.textContent); const a=Array.isArray(j)?j:[j]; const h=a.find(x=>x&&x.image); if(h){const im=Array.isArray(h.image)?h.image:[h.image]; return im.filter(Boolean);}}catch(e){}}return [];} const dom=[...ROW.querySelectorAll('img')].map(img=>img.currentSrc||img.src||img.getAttribute('data-src')||img.getAttribute('data-lazy-src')||''); const imgs=[...ldi(),...dom].filter(src=>/cf\\.bstatic\\.com\\/xdata\\/images\\/hotel|bstatic\\.com\\/xdata\\/images\\/hotel/.test(src)); return [...new Set(imgs)][3]||'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "imagen5",
            "selector": "(()=>{function ldi(){for(const s of document.querySelectorAll('script[type=\"application/ld+json\"]')){try{const j=JSON.parse(s.textContent); const a=Array.isArray(j)?j:[j]; const h=a.find(x=>x&&x.image); if(h){const im=Array.isArray(h.image)?h.image:[h.image]; return im.filter(Boolean);}}catch(e){}}return [];} const dom=[...ROW.querySelectorAll('img')].map(img=>img.currentSrc||img.src||img.getAttribute('data-src')||img.getAttribute('data-lazy-src')||''); const imgs=[...ldi(),...dom].filter(src=>/cf\\.bstatic\\.com\\/xdata\\/images\\/hotel|bstatic\\.com\\/xdata\\/images\\/hotel/.test(src)); return [...new Set(imgs)][4]||'';})()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "loop-continue-1",
      "block_type": "process",
      "title": "Loop Continue",
      "description": "Continue multi-input loop",
      "position_x": 2640,
      "position_y": 300,
      "config": {}
    }
  ],
  "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": "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-entry",
      "element_type": "group",
      "title": "Entry & Setup",
      "color": "#4589ff",
      "position_x": 48,
      "position_y": 196,
      "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": 196,
      "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": 1128,
      "position_y": 196,
      "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": 196,
      "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": 196,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "loop-continue-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Extracts Booking.com hotel detail data in Spanish from hotel detail page URLs, equivalent to the Octoparse template's 'enter URL of details pages' workflow. The starter list contains one confirmed working Booking.com hotel detail URL; replace or extend navigate.urls with additional Booking hotel detail URLs. The flow uses a multi-URL loop and appends one CSV row per detail page. Selectors are best-effort Booking.com selectors plus JSON-LD fallbacks. Booking may block automation, show CAPTCHA/consent modals, or change layout.",
      "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 1 pages. Pair with loop-continue at the end of each iteration.",
      "color": "#ee5396",
      "position_x": 680,
      "position_y": 280,
      "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: `(()=>{const labels=['Aceptar','Accept','Aceptar todo','Accept all','Estoy de acuerdo','I agree','OK'...` Verify in browser if results are empty.",
      "color": "#ee5396",
      "position_x": 1400,
      "position_y": 280,
      "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 (hotel, ubicacion, calificacion, etiqueta, descripcion). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 2480,
      "position_y": 280,
      "width": 340,
      "height": 131,
      "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": 2840,
      "position_y": 280,
      "width": 340,
      "height": 123,
      "z_index": 22,
      "data": {
        "block_id": "loop-continue-1"
      }
    }
  ]
}