/** * mercadolivre.js - Scraper para Mercado Livre * * Monitora a página de Ofertas do Dia do Mercado Livre * e extrai: nome, preço, preço original, desconto, imagem e link. */ const { config } = require('../config'); const { scrapeWithRetry } = require('../utils/browser'); /** * Executa o scraping da página de ofertas do Mercado Livre * @returns {Array} Lista de ofertas extraídas */ async function scrapeMercadoLivre() { console.log('🤝 [Mercado Livre] Iniciando scraping...'); const offers = await scrapeWithRetry(async (page) => { // Navegar para a página de ofertas await page.goto(config.urls.mercadolivre, { waitUntil: 'domcontentloaded', timeout: config.scraping.pageTimeout, }); // Aguardar carregamento dos cards de produto await page.waitForSelector('.promotion-item, .poly-card, .andes-card, [class*="poly-card"], .deal-card', { timeout: 10000, }).catch(() => { console.log(' ⚠️ [Mercado Livre] Seletores primários não encontrados, tentando alternativas...'); }); // Scroll para carregar mais itens await autoScroll(page); // Extrair dados dos produtos const products = await page.evaluate((maxOffers) => { const items = []; // Seletores para cards do Mercado Livre const cardSelectors = [ '.poly-card', '.promotion-item', '.andes-card', '[class*="poly-card"]', '.deal-card', '.ui-search-layout__item', ]; let cards = []; for (const selector of cardSelectors) { cards = document.querySelectorAll(selector); if (cards.length > 0) break; } cards.forEach((card) => { if (items.length >= maxOffers) return; try { // Nome do produto const nameEl = card.querySelector( '.poly-component__title, .promotion-item__title, .ui-search-item__title, [class*="title"], h2, h3' ); const name = nameEl ? nameEl.textContent.trim() : ''; // Preço atual const priceEl = card.querySelector( '.poly-price__current .andes-money-amount, .andes-money-amount--cents-superscript, [class*="price"], .price-tag-fraction' ); let price = ''; if (priceEl) { // Mercado Livre separa reais e centavos em elementos diferentes const fractionEl = priceEl.querySelector('.andes-money-amount__fraction, .price-tag-fraction'); const centsEl = priceEl.querySelector('.andes-money-amount__cents, .price-tag-cents'); const currencyEl = priceEl.querySelector('.andes-money-amount__currency-symbol'); if (fractionEl) { const currency = currencyEl ? currencyEl.textContent.trim() : 'R$'; const fraction = fractionEl.textContent.trim(); const cents = centsEl ? centsEl.textContent.trim() : '00'; price = `${currency} ${fraction},${cents}`; } else { price = priceEl.textContent.trim(); } } // Preço original const originalPriceEl = card.querySelector( '.andes-money-amount--previous, [class*="original-price"], s .andes-money-amount, .price-tag-deleted' ); let originalPrice = ''; if (originalPriceEl) { const origFraction = originalPriceEl.querySelector('.andes-money-amount__fraction, .price-tag-fraction'); if (origFraction) { originalPrice = `R$ ${origFraction.textContent.trim()}`; } else { originalPrice = originalPriceEl.textContent.trim(); } } // Desconto const discountEl = card.querySelector( '.poly-component__discount, [class*="discount"], .ui-search-price__second-line__label' ); const discount = discountEl ? discountEl.textContent.trim() : ''; // Imagem const imgEl = card.querySelector( 'img[src*="http2.mlstatic"], img[data-src*="http2.mlstatic"], img[src*="meli"], img' ); let image = ''; if (imgEl) { image = imgEl.getAttribute('src') || imgEl.getAttribute('data-src') || ''; // Garantir URL de imagem válida if (image && image.startsWith('data:')) { image = imgEl.getAttribute('data-src') || ''; } } // Link do produto const linkEl = card.querySelector( 'a[href*="mercadolivre.com.br"], a[href*="produto.mercadolivre"], a[href*="/MLB-"], a[href]' ); let link = linkEl ? linkEl.getAttribute('href') : ''; if (link && !link.startsWith('http')) { link = 'https://www.mercadolivre.com.br' + link; } // Cupom (ML ocasionalmente mostra cupons) const couponEl = card.querySelector( '[class*="coupon"], [class*="cupom"], [class*="highlight"]' ); let coupon = null; if (couponEl) { const couponText = couponEl.textContent.trim(); if (couponText.toLowerCase().includes('cupom') || couponText.toLowerCase().includes('off')) { coupon = couponText; } } // Frete grátis (informação extra útil) const freeShippingEl = card.querySelector( '[class*="free-shipping"], .poly-component__shipping, [class*="shipping"]' ); const hasFreeShipping = freeShippingEl && freeShippingEl.textContent.toLowerCase().includes('grátis'); if (name && price && link) { const offer = { name, price, originalPrice: originalPrice || null, discount: discount || null, image: image || null, link, coupon, store: 'mercadolivre', }; // Adicionar info de frete grátis ao cupom/observação if (hasFreeShipping && !coupon) { offer.coupon = '🚚 Frete Grátis'; } else if (hasFreeShipping && coupon) { offer.coupon += ' | 🚚 Frete Grátis'; } items.push(offer); } } catch (e) { // Ignorar cards com erro } }); return items; }, config.scraping.maxOffersPerSite); console.log(` ✅ [Mercado Livre] ${products.length} ofertas extraídas.`); return products; }, 'Mercado Livre'); return offers; } /** * Scroll automático */ async function autoScroll(page) { await page.evaluate(async () => { await new Promise((resolve) => { let totalHeight = 0; const distance = 400; const timer = setInterval(() => { window.scrollBy(0, distance); totalHeight += distance; if (totalHeight >= 3000) { clearInterval(timer); resolve(); } }, 200); }); }); await new Promise((resolve) => setTimeout(resolve, 2000)); } module.exports = { scrapeMercadoLivre };