{
  "version": "1.0.0",
  "exported_at": "2026-05-31T13:45:00.000Z",
  "project": {
    "name": "Yellow Pages Canada Scraper Product Listing",
    "description": "Scrapes YellowPages.ca business listing/detail records equivalent to the Octoparse Yellow Pages Canada product listing preview. Uses a known detail-URL loop for supermarket/Canada sample listings because the attached page analysis contains detail pages with no pagination candidates. The workflow waits for the page body, checks whether a LocalBusiness detail container exists, extracts valid business pages, and skips unavailable/non-detail pages so the loop can continue. Extend the navigate.urls array with additional YellowPages.ca detail URLs for more keyword/location results. Extracts keyword, location, business name, listing URL, address, phone, website, category, rating/status fields, and description when publicly available. If YellowPages.ca shows a CAPTCHA or rate-limit page, solve it manually before resuming.",
    "color": "bg-[#f1c21b]",
    "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.yellowpages.ca/bus/Nunavut/Whale-Cove/Issatik-Co-op/3322503.html?what=supermarket&where=Canada&useContext=true",
          "https://www.yellowpages.ca/bus/Nunavut/Rankin-Inlet/Kissarvik-Co-op-Ltd/3322610.html?what=supermarket&where=Canada&useContext=true",
          "https://www.yellowpages.ca/bus/Nunavut/Repulse-Bay/Naujat-Co-Op-Ltd/3322749.html?what=supermarket&where=Canada&useContext=true",
          "https://www.yellowpages.ca/bus/Nunavut/Gjoa-Haven/Northern-Store/4118186.html?what=supermarket&where=Canada&useContext=true",
          "https://www.yellowpages.ca/bus/Nunavut/Coral-Harbour/Katudgevik-Co-Op/3322552.html?what=supermarket&where=Canada&useContext=true",
          "https://www.yellowpages.ca/bus/Nunavut/Gjoa-Haven/Northwest-Company/7632213.html?what=supermarket&where=Canada&useContext=true",
          "https://www.yellowpages.ca/bus/Manitoba/Gillam/Gillam-Co-op-Ltd/3684735.html?what=supermarket&where=Canada&useContext=true"
        ],
        "color": "bg-[#f1c21b]"
      }
    },
    {
      "block_id": "wait-for-page-load-1",
      "block_type": "process",
      "title": "Wait for Page Load",
      "description": "Wait for page to finish loading",
      "position_x": 456,
      "position_y": 220,
      "config": {
        "timeout": 30
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 792,
      "position_y": 220,
      "config": {
        "selector": "body",
        "timeout": 30,
        "visible": true
      }
    },
    {
      "block_id": "element-exists-1",
      "block_type": "process",
      "title": "Element Exists",
      "description": "Check if element exists",
      "position_x": 1128,
      "position_y": 220,
      "config": {
        "selector": ".page__container[itemtype='http://schema.org/LocalBusiness'], h1 .merchantName, .merchantName"
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 1464,
      "position_y": 520,
      "config": {
        "rowSelector": ".page__container[itemtype='http://schema.org/LocalBusiness']",
        "fileName": "yellow-pages-canada-scraper-product-listing.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "columns": [
          {
            "name": "keyword",
            "selector": "(() => { const u = new URL(window.location.href); return u.searchParams.get('what') || ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "location",
            "selector": "(() => { const u = new URL(window.location.href); return u.searchParams.get('where') || ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "business_name",
            "selector": ".merchantName, h1 [itemprop='name']",
            "attribute": "text"
          },
          {
            "name": "detail_url",
            "selector": "(() => window.location.href)()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "canonical_url",
            "selector": "(() => { const m = document.querySelector('meta[itemprop=\"url\"]'); return m ? new URL(m.getAttribute('content'), location.origin).href : window.location.href; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "address",
            "selector": ".merchant__address[itemprop='address']",
            "attribute": "text"
          },
          {
            "name": "city",
            "selector": "[itemprop='addressLocality']",
            "attribute": "text"
          },
          {
            "name": "province",
            "selector": "[itemprop='addressRegion']",
            "attribute": "text"
          },
          {
            "name": "postal_code",
            "selector": "[itemprop='postalCode']",
            "attribute": "text"
          },
          {
            "name": "category",
            "selector": ".strat__item a",
            "attribute": "text"
          },
          {
            "name": "telephone",
            "selector": "(() => { const phone = ROW.querySelector('.mlr__item--phone .mlr__sub-text'); return phone ? phone.textContent.replace(/\\s+/g, ' ').trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "website_url",
            "selector": "(() => { const a = ROW.querySelector('.mlr__item--website a[href]'); return a ? new URL(a.getAttribute('href'), location.origin).href : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "website_text",
            "selector": "(() => { const el = ROW.querySelector('.mlr__item--website .mlr__sub-text'); return el ? el.textContent.replace(/\\s+/g, ' ').trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "rating",
            "selector": "(() => { const el = ROW.querySelector('[itemprop=\"ratingValue\"], .rating, .ypStars'); return el ? (el.getAttribute('content') || el.getAttribute('aria-label') || el.textContent || '').replace(/\\s+/g, ' ').trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "rating_count",
            "selector": "(() => { const el = ROW.querySelector('[itemprop=\"reviewCount\"], .reviewCount, .rating-count, .reviewsCount'); return el ? (el.getAttribute('content') || el.textContent || '').replace(/\\s+/g, ' ').trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "current_status",
            "selector": "(() => { const el = ROW.querySelector('.merchant__status-text'); return el ? el.textContent.replace(/\\s+/g, ' ').trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "editors_pick",
            "selector": "(() => { const el = ROW.querySelector('[class*=\"editor\" i], [class*=\"pick\" i]'); return el ? el.textContent.replace(/\\s+/g, ' ').trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "description",
            "selector": "(() => { const el = ROW.querySelector('[itemprop=\"description\"], .merchant__description, #businessDescription, .merchant__details, .details__description'); return el ? el.textContent.replace(/\\s+/g, ' ').trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 1800,
      "position_y": 520,
      "config": {
        "duration": 1
      }
    },
    {
      "block_id": "sleep-2",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 1128,
      "position_y": 520,
      "config": {
        "duration": 1
      }
    },
    {
      "block_id": "loop-continue-1",
      "block_type": "process",
      "title": "Loop Continue",
      "description": "Continue multi-input loop",
      "position_x": 2136,
      "position_y": 520,
      "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": "element-exists-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-exists-1",
      "from_connector_id": "true",
      "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"
    },
    {
      "from_block_id": "element-exists-1",
      "from_connector_id": "false",
      "to_block_id": "sleep-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-2",
      "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": 2000,
      "height": 596,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "wait-for-element-1",
          "sleep-1",
          "sleep-2"
        ]
      }
    },
    {
      "id": "group-pagination",
      "element_type": "group",
      "title": "Pagination Loop",
      "color": "#ff832b",
      "position_x": 1056,
      "position_y": 116,
      "width": 1328,
      "height": 596,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "element-exists-1",
          "loop-continue-1"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 1392,
      "position_y": 416,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "structured-export-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Scrapes YellowPages.ca business listing/detail records equivalent to the Octoparse Yellow Pages Canada product listing preview. Uses a known detail-URL loop for supermarket/Canada sample listings because the attached page analysis contains detail pages with no pagination candidates. The workflow waits for the page body, checks whether a LocalBusiness detail container exists, extracts valid business pages, and skips unavailable/non-detail pages so the loop can continue. Extend the navigate.urls array with additional YellowPages.ca detail URLs for more keyword/location results. Extracts keyword, location, business name, listing URL, address, phone, website, category, rating/status fields, and description when publicly available. If YellowPages.ca shows a CAPTCHA or rate-limit page, solve it manually before resuming.",
      "color": "#f1c21b",
      "position_x": 80,
      "position_y": 20,
      "width": 480,
      "height": 160,
      "z_index": 22,
      "data": {}
    },
    {
      "id": "note-block-element-exists-1",
      "element_type": "note",
      "title": "Note: Element Exists",
      "content": "Condition block: checks `.page__container[itemtype='http://schema.org/LocalBusiness'], h1 .merchantName, .merchantName`. True / False branches control which path runs next. Keep enough space between branches so both connector lines are visible.",
      "color": "#ee5396",
      "position_x": 1328,
      "position_y": 200,
      "width": 340,
      "height": 161,
      "z_index": 22,
      "data": {
        "block_id": "element-exists-1"
      }
    },
    {
      "id": "note-block-structured-export-1",
      "element_type": "note",
      "title": "Note: Structured Export",
      "content": "Structured export with JS columns (keyword, location, detail_url, canonical_url, telephone). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 1664,
      "position_y": 500,
      "width": 340,
      "height": 132,
      "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": 2336,
      "position_y": 500,
      "width": 340,
      "height": 123,
      "z_index": 22,
      "data": {
        "block_id": "loop-continue-1"
      }
    }
  ]
}