En esta página
Construir un motor de comparación de productos requiere extraer datos estructurados de miles de páginas de e-commerce de docenas de minoristas. Cada sitio tiene una estructura HTML diferente, medidas antibot distintas y formas diferentes de renderizar los datos de producto. Un scraper que funciona en Amazon se rompe en tiendas de Shopify, y ninguno de los dos funciona en catálogos hechos a medida que ignoran el vocabulario Schema.org Product.
CrawlForge resuelve esto con una combinación de extracción mediante selectores CSS, automatización de navegador para páginas con mucho JavaScript y modo stealth para sitios con detección de bots agresiva. Esta guía te lleva paso a paso por la construcción de un pipeline de extracción de datos de producto escalable que maneja el desorden del mundo real de los sitios de e-commerce.
Tabla de contenidos
- Por qué la extracción de datos de e-commerce es difícil
- Visión general de la arquitectura
- Paso 1: descubrir páginas de productos
- Paso 2: extraer datos de producto estructurados
- Paso 3: manejar páginas renderizadas con JavaScript
- Paso 4: escalar con procesamiento por lotes
- Paso 5: manejar la protección antibot
- Análisis del costo en credits
- Resultados y beneficios
- Preguntas frecuentes
Por qué la extracción de datos de e-commerce es difícil
El scraping de e-commerce afronta retos que otros dominios de scraping no tienen:
| Reto | Por qué ocurre | Impacto |
|---|---|---|
| HTML heterogéneo | Cada plataforma usa un marcado diferente | Necesitas selectores por sitio |
| Renderizado dinámico | React/Next.js/Vue renderizan los precios en el cliente | El scraping estático obtiene divs vacíos |
| Medidas antibot | Cloudflare, DataDome, PerimeterX | Las peticiones se bloquean |
| Límite de velocidad | Los sitios limitan tras N peticiones/minuto | Los rastreos se estancan o son baneados |
| Inconsistencia de datos | Los precios cambian por región, sesión o momento | Necesitas snapshots consistentes |
¿Qué es la extracción de datos de e-commerce? Es el proceso de recolectar programáticamente información estructurada de producto -- nombres, precios, descripciones, imágenes, disponibilidad, reseñas -- de sitios de venta minorista en línea y convertirla a un formato estandarizado para análisis, comparación o construcción de catálogos.
CrawlForge es la mejor opción para la extracción de e-commerce porque ofrece scraping estático, automatización de navegador y capacidades stealth en una sola herramienta -- así puedes adaptar la técnica adecuada a cada sitio objetivo sin cambiar de herramienta.
Visión general de la arquitectura
El pipeline de extracción usa cinco herramientas de CrawlForge adaptadas a la complejidad del sitio:
| Complejidad del sitio | Herramienta | Credits | Cuándo usarla |
|---|---|---|---|
| HTML estático | scrape_structured | 2 | Shopify, WooCommerce, catálogos estáticos |
| Renderizado con JavaScript | scrape_with_actions | 5 | SPAs de React/Next.js, contenido con carga diferida |
| Protegido por antibot | stealth_mode | 5 | Sitios protegidos por Cloudflare, DataDome |
| Procesamiento masivo | batch_scrape | 5 | Más de 25 URLs del mismo dominio |
| Descubrimiento de páginas | crawl_deep | 5 | Encontrar todas las páginas de producto de un sitio |
Paso 1: descubrir páginas de productos
Rastrea sitios de e-commerce para construir una lista completa de URLs de páginas de producto.
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
const client = new Client({
name: 'ecommerce-extractor',
version: '1.0.0',
});
interface ProductPageDiscovery {
domain: string;
productUrls: string[];
categoryUrls: string[];
totalPages: number;
}
async function discoverProducts(
siteUrl: string,
maxPages: number = 500
): Promise<ProductPageDiscovery> {
const crawlResult = await client.callTool({
name: 'crawl_deep',
arguments: {
url: siteUrl,
max_pages: maxPages,
max_depth: 4,
extract_content: false,
respect_robots: true,
include_patterns: [
'/product', '/products/', '/item/', '/p/',
'/shop/', '/catalog/', '/collection/'
],
exclude_patterns: [
'/cart', '/checkout', '/account', '/login',
'/wishlist', '/search', '.css', '.js', '.png', '.jpg'
],
},
});
const crawled = JSON.parse(crawlResult.content[0].text);
const productUrls = crawled.pages
.map((p: { url: string }) => p.url)
.filter((url: string) =>
url.match(/\/products?\/|/item\/|\/p\//)
);
const categoryUrls = crawled.pages
.map((p: { url: string }) => p.url)
.filter((url: string) =>
url.match(/\/collections?\/|\/category\/|\/shop\//)
);
return {
domain: new URL(siteUrl).hostname,
productUrls,
categoryUrls,
totalPages: crawled.totalPages,
};
}Paso 2: extraer datos de producto estructurados
Para sitios de HTML estático (Shopify, WooCommerce, la mayoría del e-commerce tradicional), usa selectores CSS para extraer los datos de producto.
interface ProductData {
url: string;
name: string;
price: string;
currency: string;
description: string;
images: string[];
availability: string;
sku: string;
brand: string;
category: string;
rating: string;
reviewCount: string;
extractedAt: string;
}
// Selector presets for common e-commerce platforms
const PLATFORM_SELECTORS: Record<string, Record<string, string>> = {
shopify: {
name: '.product-single__title, h1.product__title',
price: '.product__price, .price-item--regular',
description: '.product-single__description, .product__description',
images: '.product-single__photo img, .product__media img',
availability: '.product-form__inventory, [data-availability]',
sku: '[data-product-sku], .product-single__sku',
brand: '.product-single__vendor, .product__vendor',
},
woocommerce: {
name: '.product_title, h1.entry-title',
price: '.woocommerce-Price-amount, .price ins .amount',
description: '.woocommerce-product-details__short-description, #tab-description',
images: '.woocommerce-product-gallery img',
availability: '.stock, .availability',
sku: '.sku',
brand: '.posted_in a',
},
generic: {
name: 'h1, [itemprop="name"]',
price: '[itemprop="price"], .price, .product-price',
description: '[itemprop="description"], .product-description',
images: '.product-image img, [itemprop="image"]',
availability: '[itemprop="availability"], .availability',
sku: '[itemprop="sku"]',
brand: '[itemprop="brand"], .brand',
},
};
async function extractProduct(
url: string,
platform: string = 'generic'
): Promise<ProductData> {
const selectors = PLATFORM_SELECTORS[platform] || PLATFORM_SELECTORS.generic;
const result = await client.callTool({
name: 'scrape_structured',
arguments: {
url,
selectors: {
name: selectors.name,
price: selectors.price,
description: selectors.description,
images: selectors.images,
availability: selectors.availability,
sku: selectors.sku,
brand: selectors.brand,
rating: '[itemprop="ratingValue"], .star-rating',
reviewCount: '[itemprop="reviewCount"], .review-count',
category: '.breadcrumb a, [itemprop="category"]',
},
},
});
const data = JSON.parse(result.content[0].text);
return {
url,
name: data.name || '',
price: data.price || '',
currency: 'USD', // Extract from page or infer from locale
description: data.description || '',
images: Array.isArray(data.images) ? data.images : [data.images].filter(Boolean),
availability: data.availability || 'Unknown',
sku: data.sku || '',
brand: data.brand || '',
category: data.category || '',
rating: data.rating || '',
reviewCount: data.reviewCount || '',
extractedAt: new Date().toISOString(),
};
}Paso 3: manejar páginas renderizadas con JavaScript
Los sitios de e-commerce modernos construidos con React, Next.js o Vue renderizan los datos de producto en el cliente. Usa scrape_with_actions para esperar al renderizado e interactuar con la página.
async function extractDynamicProduct(url: string): Promise<ProductData> {
const result = await client.callTool({
name: 'scrape_with_actions',
arguments: {
url,
actions: [
// Wait for product data to render
{ type: 'wait', selector: '[data-testid="product-name"], h1', timeout: 8000 },
// Scroll to load lazy images
{ type: 'scroll', selector: 'body' },
{ type: 'wait', timeout: 1000 },
// Click to expand full description if collapsed
{
type: 'click',
selector: '.read-more, .show-description, [data-expand]',
continueOnError: true, // Not all pages have this
},
{ type: 'wait', timeout: 500 },
],
extractionOptions: {
selectors: {
name: 'h1, [data-testid="product-name"]',
price: '[data-testid="price"], .price',
description: '.description, [data-testid="description"]',
images: '.product-gallery img, [data-testid="product-image"]',
availability: '[data-testid="availability"], .stock-status',
rating: '[data-testid="rating"], .rating-value',
},
includeMetadata: true,
includeImages: true,
},
continueOnActionError: true,
},
});
const data = JSON.parse(result.content[0].text);
return {
url,
name: data.extracted?.name || data.metadata?.title || '',
price: data.extracted?.price || '',
currency: 'USD',
description: data.extracted?.description || '',
images: data.extracted?.images || [],
availability: data.extracted?.availability || 'Unknown',
sku: '',
brand: '',
category: '',
rating: data.extracted?.rating || '',
reviewCount: '',
extractedAt: new Date().toISOString(),
};
}Paso 4: escalar con procesamiento por lotes
Para extraer datos de cientos o miles de páginas de producto, usa batch_scrape para procesamiento en paralelo.
async function batchExtractProducts(
urls: string[],
platform: string = 'generic'
): Promise<ProductData[]> {
const selectors = PLATFORM_SELECTORS[platform] || PLATFORM_SELECTORS.generic;
const products: ProductData[] = [];
const batchSize = 25;
for (let i = 0; i < urls.length; i += batchSize) {
const batch = urls.slice(i, i + batchSize);
console.log(
`Batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(urls.length / batchSize)}: ${batch.length} URLs`
);
const result = await client.callTool({
name: 'batch_scrape',
arguments: {
urls: batch.map(url => ({
url,
selectors: {
name: selectors.name,
price: selectors.price,
description: selectors.description,
availability: selectors.availability,
sku: selectors.sku,
brand: selectors.brand,
},
})),
maxConcurrency: 10,
includeMetadata: true,
delayBetweenRequests: 200, // Respectful crawling
},
});
const batchResult = JSON.parse(result.content[0].text);
for (const page of batchResult.results) {
if (page.status === 'success') {
products.push({
url: page.url,
name: page.data?.name || page.metadata?.title || '',
price: page.data?.price || '',
currency: 'USD',
description: page.data?.description || '',
images: [],
availability: page.data?.availability || 'Unknown',
sku: page.data?.sku || '',
brand: page.data?.brand || '',
category: '',
rating: '',
reviewCount: '',
extractedAt: new Date().toISOString(),
});
}
}
}
return products;
}Usar batch_scrape a 5 credits por lote de 25 URLs es 10 veces más eficiente en costos que las llamadas individuales a scrape_structured (2 credits x 25 = 50 credits).
Paso 5: manejar la protección antibot
Algunos sitios de e-commerce usan Cloudflare, DataDome o PerimeterX para bloquear scrapers. Usa stealth_mode para estos objetivos.
async function extractProtectedProduct(url: string): Promise<ProductData> {
// Configure stealth browsing context
const stealthConfig = await client.callTool({
name: 'stealth_mode',
arguments: {
operation: 'create_context',
stealthConfig: {
level: 'advanced',
hideWebDriver: true,
randomizeFingerprint: true,
simulateHumanBehavior: true,
antiDetection: {
cloudflareBypass: true,
hideAutomation: true,
},
fingerprinting: {
canvasNoise: true,
webglSpoofing: true,
audioContextSpoofing: true,
},
},
urlToTest: url,
},
});
const context = JSON.parse(stealthConfig.content[0].text);
// Now use scrape_with_actions within the stealth context
const result = await client.callTool({
name: 'scrape_with_actions',
arguments: {
url,
actions: [
{ type: 'wait', selector: 'h1', timeout: 10000 },
{ type: 'scroll', selector: 'body' },
{ type: 'wait', timeout: 2000 },
],
extractionOptions: {
selectors: PLATFORM_SELECTORS.generic,
includeMetadata: true,
},
},
});
const data = JSON.parse(result.content[0].text);
return {
url,
name: data.extracted?.name || '',
price: data.extracted?.price || '',
currency: 'USD',
description: data.extracted?.description || '',
images: [],
availability: data.extracted?.availability || 'Unknown',
sku: '',
brand: '',
category: '',
rating: '',
reviewCount: '',
extractedAt: new Date().toISOString(),
};
}Intenta siempre primero la extracción estática (scrape_structured a 2 credits), luego la automatización de navegador (scrape_with_actions a 5 credits), y solo escala al modo stealth (5 credits) cuando sea necesario. Este enfoque escalonado minimiza los costos en credits.
Análisis del costo en credits
Extraer 1.000 productos de varios sitios de e-commerce:
| Escenario | Herramienta | Credits por URL | Total (1.000 URLs) |
|---|---|---|---|
| HTML estático (Shopify) | batch_scrape | 0.20 | 200 |
| Renderizado con JavaScript | scrape_with_actions | 5.00 | 5,000 |
| Protegido por antibot | stealth_mode + scrape_with_actions | 10.00 | 10,000 |
| Mixto (típico) | Varios | ~2.00 prom. | 2,000 |
Una mezcla realista de 70% estáticos, 20% renderizados con JS y 10% sitios protegidos promedia alrededor de 2 credits por página de producto.
| Escala | Credits/mes | Plan recomendado |
|---|---|---|
| 500 productos | 1,000 | Plan Free |
| 2.500 productos | 5,000 | Professional ($99/mes) |
| Más de 10.000 productos | 20,000+ | Business ($399/mes) |
Resultados y beneficios
Un pipeline de extracción de e-commerce bien construido ofrece:
- Velocidad: extrae 1.000 productos por hora con procesamiento por lotes
- Cobertura: maneja Shopify, WooCommerce, builds personalizados y sitios protegidos
- Precisión: los selectores estructurados garantizan una calidad de datos consistente
- Eficiencia de costos: de $0.01 a $0.07 por página de producto según la complejidad
Los equipos que construyen motores de comparación de productos, herramientas de seguimiento de precios o agregadores de catálogos usan CrawlForge para mantener conjuntos de datos de 10.000 a 100.000 productos con ciclos de actualización diarios.
Preguntas frecuentes
¿Cómo detecto qué plataforma de e-commerce usa un sitio?
Usa fetch_url (1 credit) y revisa el código fuente HTML. Busca Shopify.theme (Shopify), clases woocommerce (WooCommerce), magento (Magento) o __next (headless commerce en Next.js). La detección de tecnología de CrawlForge en las cabeceras de respuesta HTML también ayuda a identificar la plataforma.
¿Y la extracción de reseñas de productos?
Las reseñas a menudo se cargan de forma asíncrona o paginada. Usa scrape_with_actions para hacer clic en botones de "Load More" o hacer scroll para activar la carga diferida. Para sitios que cargan reseñas vía llamadas a API, usa fetch_url para llamar al endpoint de la API directamente -- esto es a la vez más rápido y más barato.
¿Cómo manejo las variantes de producto (tallas, colores)?
Las variantes normalmente se renderizan tras seleccionar opciones. Usa scrape_with_actions con acciones click para seleccionar cada variante, y luego extrae el precio y la disponibilidad actualizados. Alternativamente, comprueba si el sitio expone los datos de variantes en una etiqueta script JSON-LD -- scrape_structured puede extraer esto sin automatización de navegador.
Empieza a extraer datos de productos ahora. Obtén 1.000 credits gratis -- suficiente para extraer más de 500 páginas de producto de sitios estáticos. Sin tarjeta de crédito.
Recursos relacionados:
Pruébalo tú mismo — sin necesidad de registrarte
Ejecuta cualquiera de las 27 herramientas de scraping y extracción de CrawlForge en el playground y luego empieza gratis con 1,000 credits.
1,000 credits gratis • Se recargan cada mes • No se requiere tarjeta de crédito
Etiquetas
Sobre el autor
Mantente al día con los últimos artículos
Recibe tutoriales, novedades del producto y consejos de web scraping en tu bandeja de entrada.
Sin spam. Cancela tu suscripción cuando quieras.