{
  "version": "1.0.0",
  "exported_at": "2026-05-31T00:00:00.000Z",
  "project": {
    "name": "Bookingcom Listings Scraper",
    "description": "Scrapes Booking.com accommodation search/listing results for the keyword 北海道. Extracts keyword, hotel name, hotel URL, address/location when exposed in the card, rating label, review count, and review score. Pagination strategy: uses Booking.com's predictable search result offset URLs in a navigate.urls loop (offset=0,25,50...) and appends each page to one CSV, avoiding fragile Next-button/calendar overlay clicks. Best effort: Booking.com may show anti-bot/CAPTCHA, regional/language variations, or changed frontend selectors; some cards may not expose address text in search results.",
    "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": 220,
      "config": {
        "urls": [
          "https://www.booking.com/searchresults.html?ss=%E5%8C%97%E6%B5%B7%E9%81%93&dest_id=2347&dest_type=region&group_adults=2&no_rooms=1&group_children=0&sr_order=popularity&offset=0",
          "https://www.booking.com/searchresults.html?ss=%E5%8C%97%E6%B5%B7%E9%81%93&dest_id=2347&dest_type=region&group_adults=2&no_rooms=1&group_children=0&sr_order=popularity&offset=25",
          "https://www.booking.com/searchresults.html?ss=%E5%8C%97%E6%B5%B7%E9%81%93&dest_id=2347&dest_type=region&group_adults=2&no_rooms=1&group_children=0&sr_order=popularity&offset=50",
          "https://www.booking.com/searchresults.html?ss=%E5%8C%97%E6%B5%B7%E9%81%93&dest_id=2347&dest_type=region&group_adults=2&no_rooms=1&group_children=0&sr_order=popularity&offset=75",
          "https://www.booking.com/searchresults.html?ss=%E5%8C%97%E6%B5%B7%E9%81%93&dest_id=2347&dest_type=region&group_adults=2&no_rooms=1&group_children=0&sr_order=popularity&offset=100",
          "https://www.booking.com/searchresults.html?ss=%E5%8C%97%E6%B5%B7%E9%81%93&dest_id=2347&dest_type=region&group_adults=2&no_rooms=1&group_children=0&sr_order=popularity&offset=125",
          "https://www.booking.com/searchresults.html?ss=%E5%8C%97%E6%B5%B7%E9%81%93&dest_id=2347&dest_type=region&group_adults=2&no_rooms=1&group_children=0&sr_order=popularity&offset=150",
          "https://www.booking.com/searchresults.html?ss=%E5%8C%97%E6%B5%B7%E9%81%93&dest_id=2347&dest_type=region&group_adults=2&no_rooms=1&group_children=0&sr_order=popularity&offset=175",
          "https://www.booking.com/searchresults.html?ss=%E5%8C%97%E6%B5%B7%E9%81%93&dest_id=2347&dest_type=region&group_adults=2&no_rooms=1&group_children=0&sr_order=popularity&offset=200",
          "https://www.booking.com/searchresults.html?ss=%E5%8C%97%E6%B5%B7%E9%81%93&dest_id=2347&dest_type=region&group_adults=2&no_rooms=1&group_children=0&sr_order=popularity&offset=225"
        ],
        "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": 220,
      "config": {
        "timeout": 45
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 840,
      "position_y": 220,
      "config": {
        "selector": "[data-testid=\"property-card\"]",
        "timeout": 45,
        "visible": true
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 1200,
      "position_y": 220,
      "config": {
        "rowSelector": "[data-testid=\"property-card\"]",
        "fileName": "booking-com-hotel-listings-scraper.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "columns": [
          {
            "name": "keyword",
            "selector": "\"北海道\"",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "hotel_name",
            "selector": "[data-testid=\"title\"]",
            "attribute": "text"
          },
          {
            "name": "hotel_url",
            "selector": "a[data-testid=\"title-link\"]",
            "attribute": "href"
          },
          {
            "name": "address",
            "selector": "(() => { const clean = s => (s || '').replace(/\\s+/g, ' ').replace(/\\s*(Show on map|show map|地図に表示).*$/i, '').trim(); const directSelectors = ['[data-testid=\"address\"]', '[data-testid=\"location\"]', '[data-testid=\"location-link\"]', '[data-testid=\"property-card-location\"]', '[data-testid=\"distance\"]']; for (const sel of directSelectors) { const v = clean(ROW.querySelector(sel)?.innerText || ROW.querySelector(sel)?.getAttribute('aria-label') || ROW.querySelector(sel)?.getAttribute('title')); if (v) return v; } const aria = Array.from(ROW.querySelectorAll('[aria-label], [title]')).map(e => clean(e.getAttribute('aria-label') || e.getAttribute('title'))).find(v => v && /(show map|show on map|地図|location|map|中心|centre|center|駅|station|北海道|hokkaido|japan|市|町|区|郡)/i.test(v)); if (aria) return clean(aria); const title = clean(ROW.querySelector('[data-testid=\"title\"]')?.innerText); const lines = (ROW.innerText || '').split(/\\n+/).map(clean).filter(Boolean); for (let i = 0; i < lines.length; i++) { if (/^(Show on map|show map|地図に表示)$/i.test(lines[i]) && i > 0) return clean(lines[i - 1]); if (/(Show on map|show map|地図に表示)/i.test(lines[i])) { const v = clean(lines[i]); if (v) return v; } } const bad = /(Scored|Rated|reviews?|クチコミ|件|Excellent|Very Good|Good|Wonderful|Superb|Pleasant|Review score|Price|night|料金|Sign in|Genius|Travel Sustainable|Breakfast|Show prices|See availability|Only \\d+|left|Free cancellation|No prepayment|Reserve|stars?|ホテル|Hotel)$/i; const candidates = lines.filter(l => l !== title && l.length > 1 && l.length < 120 && !bad.test(l) && !/^\\d+(\\.\\d+)?$/.test(l) && !/^[¥$€£]/.test(l)); const locationLike = candidates.find(l => /(中心|centre|center|駅|Station|Otaru|Sapporo|Hakodate|Furano|Asahikawa|Abashiri|Hokuto|Tomakomai|Hokkaido|北海道|Japan|市|町|区|郡|km|miles?)/i.test(l)); return locationLike || ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "rating",
            "selector": "(() => { const t = (ROW.querySelector('[data-testid=\"review-score\"]')?.innerText || '').replace(/\\s+/g, ' ').trim(); const direct = Array.from(ROW.querySelectorAll('[data-testid=\"review-score\"] div, [data-testid=\"review-score\"] span')).map(e => e.innerText.trim()).filter(Boolean).find(v => /^(Very Good|Excellent|Good|Wonderful|Superb|Pleasant|Review score|とても良い|すばらしい|良い|最高|満足)$/i.test(v)); if (direct) return direct; const m = t.match(/Rated\\s+([A-Za-z ]+?)(?:\\s+\\d|$)/i) || t.match(/(Very Good|Excellent|Good|Wonderful|Superb|Pleasant|Review score|とても良い|すばらしい|良い|最高|満足)/i); return m ? (m[1] || m[0]).trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "reviews",
            "selector": "(() => { const t = (ROW.querySelector('[data-testid=\"review-score\"]')?.innerText || ROW.innerText || '').replace(/\\s+/g, ' ').trim(); const m = t.match(/([\\d,]+)\\s*(?:reviews|件|クチコミ)/i); return m ? m[1] : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "review_score",
            "selector": "(() => { const t = (ROW.querySelector('[data-testid=\"review-score\"]')?.innerText || '').replace(/\\s+/g, ' ').trim(); const m = t.match(/Scored\\s*([\\d.]+)/i) || t.match(/(?:スコア|評価)\\s*([\\d.]+)/i) || t.match(/\\b(\\d+(?:\\.\\d+)?)\\b/); return m ? m[1] : ''; })()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 1560,
      "position_y": 220,
      "config": {
        "duration": 2
      }
    },
    {
      "block_id": "loop-continue-1",
      "block_type": "process",
      "title": "Loop Continue",
      "description": "Continue multi-input loop",
      "position_x": 1920,
      "position_y": 220,
      "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": "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": "sleep-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-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": 116,
      "width": 1760,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "wait-for-element-1",
          "sleep-1"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 1128,
      "position_y": 116,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "structured-export-1"
        ]
      }
    },
    {
      "id": "group-pagination",
      "element_type": "group",
      "title": "Pagination Loop",
      "color": "#ff832b",
      "position_x": 1848,
      "position_y": 116,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "loop-continue-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Scrapes Booking.com accommodation search/listing results for the keyword 北海道. Extracts keyword, hotel name, hotel URL, address/location when exposed in the card, rating label, review count, and review score. Pagination strategy: uses Booking.com's predictable search result offset URLs in a navigate.urls loop (offset=0,25,50...) and appends each page to one CSV, avoiding fragile Next-button/calendar overlay clicks. Best effort: Booking.com may show anti-bot/CAPTCHA, regional/language variations, or changed frontend selectors; some cards may not expose address text in search results.",
      "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 10 pages. Pair with loop-continue at the end of each iteration.",
      "color": "#ee5396",
      "position_x": 320,
      "position_y": 200,
      "width": 332,
      "height": 107,
      "z_index": 22,
      "data": {
        "block_id": "navigate-1"
      }
    },
    {
      "id": "note-block-structured-export-1",
      "element_type": "note",
      "title": "Note: Structured Export",
      "content": "Structured export with JS columns (keyword, address, rating, reviews, review_score). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 1400,
      "position_y": 200,
      "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": 2120,
      "position_y": 200,
      "width": 340,
      "height": 123,
      "z_index": 22,
      "data": {
        "block_id": "loop-continue-1"
      }
    }
  ]
}