AutomationFlowsData & Sheets › Domfasad Product Scraper

Domfasad Product Scraper

DomFasad Product Scraper. Uses start, httpRequest, postgres. Manual trigger; 9 nodes.

Manual trigger★★★★☆ complexity9 nodesStartHTTP RequestPostgres
Data & Sheets Trigger: Manual Nodes: 9 Complexity: ★★★★☆ Added:

This workflow follows the HTTP Request → Postgres recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "DomFasad Product Scraper",
  "nodes": [
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        250,
        300
      ]
    },
    {
      "parameters": {
        "values": {
          "string": [
            {
              "name": "base_url",
              "value": "https://domfasad.com.ua"
            },
            {
              "name": "category_url",
              "value": "/ua/terrasnaya-doska-dpk/"
            }
          ]
        }
      },
      "name": "Set Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        450,
        300
      ]
    },
    {
      "parameters": {
        "url": "={{$node[\"Set Config\"].json[\"base_url\"]}}{{$node[\"Set Config\"].json[\"category_url\"]}}",
        "options": {
          "headerParameters": {
            "parameters": [
              {
                "name": "User-Agent",
                "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
              },
              {
                "name": "Accept-Language",
                "value": "uk-UA,uk;q=0.9,ru;q=0.8,en;q=0.7"
              }
            ]
          }
        }
      },
      "name": "Fetch Category Page",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        650,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parse HTML and extract product URLs using Cheerio\nconst cheerio = require('cheerio');\nconst html = $input.item.json.data;\nconst $ = cheerio.load(html);\n\nconst products = [];\n\n// DomFasad uses product cards with specific selectors\n$('.product-card, .product-item, [data-product-id]').each((i, el) => {\n  const $el = $(el);\n  \n  // Extract product URL\n  const link = $el.find('a.product-link, a[href*=\"/terrasnaya-doska/\"]').first().attr('href');\n  \n  if (link) {\n    products.push({\n      url: link.startsWith('http') ? link : `https://domfasad.com.ua${link}`,\n      source: 'domfasad'\n    });\n  }\n});\n\n// Alternative selector if primary doesn't work\nif (products.length === 0) {\n  $('a[href*=\"/terrasnaya-doska/\"]').each((i, el) => {\n    const href = $(el).attr('href');\n    if (href && href.includes('/terrasnaya-doska/') && /\\d+/.test(href)) {\n      products.push({\n        url: href.startsWith('http') ? href : `https://domfasad.com.ua${href}`,\n        source: 'domfasad'\n      });\n    }\n  });\n}\n\n// Remove duplicates\nconst uniqueProducts = [...new Map(products.map(p => [p.url, p])).values()];\n\nconsole.log(`Found ${uniqueProducts.length} products`);\n\nreturn uniqueProducts.map(p => ({json: p}));"
      },
      "name": "Extract Product URLs",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        850,
        300
      ]
    },
    {
      "parameters": {
        "batchSize": 5,
        "options": {
          "waitBetweenBatches": 2000
        }
      },
      "name": "Loop Over Products",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        1050,
        300
      ]
    },
    {
      "parameters": {
        "url": "={{$json[\"url\"]}}",
        "options": {
          "headerParameters": {
            "parameters": [
              {
                "name": "User-Agent",
                "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
              }
            ]
          }
        }
      },
      "name": "Fetch Product Page",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        1250,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parse individual product page\nconst cheerio = require('cheerio');\nconst html = $input.item.json.data;\nconst $ = cheerio.load(html);\nconst url = $input.item.json.url;\n\nconst product = {\n  source: 'domfasad',\n  external_id: url.match(/\\/(\\d+)\\/?$/)?.[1] || url.split('/').filter(Boolean).pop(),\n  external_url: url,\n  name_ua: '',\n  name_ru: '',\n  description_ua: '',\n  price: null,\n  old_price: null,\n  unit: '\u0448\u0442',\n  images: [],\n  attributes: {},\n  in_stock: true,\n  brand: '',\n  sku: ''\n};\n\n// Extract title\nproduct.name_ua = $('h1').first().text().trim();\nproduct.name_ru = product.name_ua; // Fallback\n\n// Extract price\nconst priceText = $('.price-new, .product-price, [data-price]').first().text();\nif (priceText) {\n  product.price = parseFloat(priceText.replace(/[^\\d.,]/g, '').replace(',', '.'));\n}\n\nconst oldPriceText = $('.price-old, .old-price').first().text();\nif (oldPriceText) {\n  product.old_price = parseFloat(oldPriceText.replace(/[^\\d.,]/g, '').replace(',', '.'));\n}\n\n// Extract description\nconst desc = $('.product-description, #tab-description, .description').first().html();\nif (desc) {\n  product.description_ua = desc.trim();\n}\n\n// Extract images\n$('.product-image img, .product-gallery img, [data-image]').each((i, el) => {\n  const src = $(el).attr('src') || $(el).attr('data-src') || $(el).attr('data-image');\n  if (src && !src.includes('placeholder')) {\n    const fullUrl = src.startsWith('http') ? src : `https://domfasad.com.ua${src}`;\n    if (!product.images.includes(fullUrl)) {\n      product.images.push(fullUrl);\n    }\n  }\n});\n\n// Extract SKU/Article\nconst skuText = $('.sku, .article, [data-sku]').first().text();\nif (skuText) {\n  product.sku = skuText.replace(/[^A-Z0-9-]/gi, '').trim();\n}\n\n// Extract brand\nconst brandText = $('.brand, [data-brand], .manufacturer').first().text();\nif (brandText) {\n  product.brand = brandText.trim();\n}\n\n// Extract attributes/specifications\n$('.product-specs tr, .specifications tr, .attributes tr').each((i, el) => {\n  const $row = $(el);\n  const key = $row.find('td:first-child, th').text().trim();\n  const value = $row.find('td:last-child').text().trim();\n  \n  if (key && value && key !== value) {\n    // Normalize keys\n    const normalizedKey = key.toLowerCase()\n      .replace(/[^a-z\u0430-\u044f0-9]+/g, '_')\n      .replace(/^_|_$/g, '');\n    product.attributes[normalizedKey] = value;\n  }\n});\n\n// Check stock status\nconst stockIndicator = $('.out-of-stock, .not-available').length;\nproduct.in_stock = stockIndicator === 0;\n\nreturn [{json: product}];"
      },
      "name": "Parse Product Data",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1450,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO product_sources (product_id, source_id, external_id, external_url, external_price, external_name, raw_data, last_scraped_at)\nVALUES (\n  (SELECT id FROM products WHERE slug = $1 LIMIT 1),\n  (SELECT id FROM sources WHERE name = 'domfasad'),\n  $2,\n  $3,\n  $4,\n  $5,\n  $6::jsonb,\n  NOW()\n)\nON CONFLICT (source_id, external_id) DO UPDATE SET\n  external_price = EXCLUDED.external_price,\n  external_name = EXCLUDED.external_name,\n  raw_data = EXCLUDED.raw_data,\n  last_scraped_at = NOW()\nRETURNING *;",
        "additionalFields": {
          "queryParameters": "={{ [\n  $json.slug || $json.external_id,\n  $json.external_id,\n  $json.external_url,\n  $json.price,\n  $json.name_ua,\n  JSON.stringify($json)\n] }}"
        }
      },
      "name": "Save to PostgreSQL",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        1650,
        300
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{$node[\"Loop Over Products\"].json[\"completed\"]}}",
              "value2": true
            }
          ]
        }
      },
      "name": "Check If Done",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        1850,
        300
      ]
    }
  ],
  "connections": {
    "Start": {
      "main": [
        [
          {
            "node": "Set Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Config": {
      "main": [
        [
          {
            "node": "Fetch Category Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Category Page": {
      "main": [
        [
          {
            "node": "Extract Product URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Product URLs": {
      "main": [
        [
          {
            "node": "Loop Over Products",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Products": {
      "main": [
        [
          {
            "node": "Fetch Product Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Product Page": {
      "main": [
        [
          {
            "node": "Parse Product Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Product Data": {
      "main": [
        [
          {
            "node": "Save to PostgreSQL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save to PostgreSQL": {
      "main": [
        [
          {
            "node": "Check If Done",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check If Done": {
      "main": [
        [],
        [
          {
            "node": "Loop Over Products",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {},
  "staticData": null,
  "tags": [
    "scraping",
    "domfasad",
    "products"
  ]
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

DomFasad Product Scraper. Uses start, httpRequest, postgres. Manual trigger; 9 nodes.

Source: https://github.com/EpicStarAi/rocky-builder/blob/e587644e3e6e1827c76d1892d68e3814cc149146/n8n-workflows/domfasad-scraper.json — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

External Integrations Workflow. Uses start, httpRequest, mongoDb, postgres. Manual trigger; 9 nodes.

Start, HTTP Request, MongoDB +4
Data & Sheets

Traduzir Dados da Obra e Preparar para Supabase. Uses start, httpRequest. Manual trigger; 11 nodes.

Start, HTTP Request
Data & Sheets

Postgres: insert update executeQuery. Uses start, postgres. Manual trigger; 7 nodes.

Start, Postgres
Data & Sheets

BTC_Poll. Uses start, httpRequest, postgres. Scheduled trigger; 5 nodes.

Start, HTTP Request, Postgres
Data & Sheets

Reagendamiento_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 89 nodes.

Execute Workflow Trigger, Redis, HTTP Request +3