Node modules retirado

This commit is contained in:
ZyperCH
2026-06-02 20:35:21 -03:00
parent eff29f6393
commit 0ccebcedac
11 changed files with 1981 additions and 1981 deletions

View File

@@ -1,2 +1,2 @@
[Desktop Entry] [Desktop Entry]
Icon=folder-games Icon=folder-games

284
.gitignore vendored
View File

@@ -1,143 +1,143 @@
# Logs # Logs
logs logs
*.log *.log
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
lerna-debug.log* lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html) # Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data # Runtime data
pids pids
*.pid *.pid
*.seed *.seed
*.pid.lock *.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover # Directory for instrumented libs generated by jscoverage/JSCover
lib-cov lib-cov
# Coverage directory used by tools like istanbul # Coverage directory used by tools like istanbul
coverage coverage
*.lcov *.lcov
# nyc test coverage # nyc test coverage
.nyc_output .nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt .grunt
# Bower dependency directory (https://bower.io/) # Bower dependency directory (https://bower.io/)
bower_components bower_components
# node-waf configuration # node-waf configuration
.lock-wscript .lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html) # Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release build/Release
# Dependency directories # Dependency directories
node_modules/ node_modules/
jspm_packages/ jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/) # Snowpack dependency directory (https://snowpack.dev/)
web_modules/ web_modules/
# TypeScript cache # TypeScript cache
*.tsbuildinfo *.tsbuildinfo
# Optional npm cache directory # Optional npm cache directory
.npm .npm
# Optional eslint cache # Optional eslint cache
.eslintcache .eslintcache
# Optional stylelint cache # Optional stylelint cache
.stylelintcache .stylelintcache
# Optional REPL history # Optional REPL history
.node_repl_history .node_repl_history
# Output of 'npm pack' # Output of 'npm pack'
*.tgz *.tgz
# Yarn Integrity file # Yarn Integrity file
.yarn-integrity .yarn-integrity
# dotenv environment variable files # dotenv environment variable files
.env .env
.env.* .env.*
!.env.example !.env.example
# parcel-bundler cache (https://parceljs.org/) # parcel-bundler cache (https://parceljs.org/)
.cache .cache
.parcel-cache .parcel-cache
# Next.js build output # Next.js build output
.next .next
out out
# Nuxt.js build / generate output # Nuxt.js build / generate output
.nuxt .nuxt
dist dist
.output .output
# Gatsby files # Gatsby files
.cache/ .cache/
# Comment in the public line in if your project uses Gatsby and not Next.js # Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support # https://nextjs.org/blog/next-9-1#public-directory-support
# public # public
# vuepress build output # vuepress build output
.vuepress/dist .vuepress/dist
# vuepress v2.x temp directory # vuepress v2.x temp directory
.temp .temp
# Sveltekit cache directory # Sveltekit cache directory
.svelte-kit/ .svelte-kit/
# vitepress build output # vitepress build output
**/.vitepress/dist **/.vitepress/dist
# vitepress cache directory # vitepress cache directory
**/.vitepress/cache **/.vitepress/cache
# Docusaurus cache and generated files # Docusaurus cache and generated files
.docusaurus .docusaurus
# Serverless directories # Serverless directories
.serverless/ .serverless/
# FuseBox cache # FuseBox cache
.fusebox/ .fusebox/
# DynamoDB Local files # DynamoDB Local files
.dynamodb/ .dynamodb/
# Firebase cache directory # Firebase cache directory
.firebase/ .firebase/
# TernJS port file # TernJS port file
.tern-port .tern-port
# Stores VSCode versions used for testing VSCode extensions # Stores VSCode versions used for testing VSCode extensions
.vscode-test .vscode-test
# pnpm # pnpm
.pnpm-store .pnpm-store
# yarn v3 # yarn v3
.pnp.* .pnp.*
.yarn/* .yarn/*
!.yarn/patches !.yarn/patches
!.yarn/plugins !.yarn/plugins
!.yarn/releases !.yarn/releases
!.yarn/sdks !.yarn/sdks
!.yarn/versions !.yarn/versions
# Vite files # Vite files
vite.config.js.timestamp-* vite.config.js.timestamp-*
vite.config.ts.timestamp-* vite.config.ts.timestamp-*
.vite/ .vite/

3102
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,19 @@
{ {
"dependencies": { "dependencies": {
"axios": "^1.16.1", "axios": "^1.16.1",
"puppeteer": "^25.0.4", "puppeteer": "^25.0.4",
"puppeteer-extra": "^3.3.6", "puppeteer-extra": "^3.3.6",
"puppeteer-extra-plugin-stealth": "^2.11.2" "puppeteer-extra-plugin-stealth": "^2.11.2"
}, },
"name": "codevscodesjs", "name": "codevscodesjs",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"type": "commonjs" "type": "commonjs"
} }

View File

@@ -1,31 +1,31 @@
const ProductEntity = require('../domain/ProductEntity'); const ProductEntity = require('../domain/ProductEntity');
class ProcessPromotionsUseCase { class ProcessPromotionsUseCase {
constructor(scraper, affiliateGenerator, apiClient) { constructor(scraper, affiliateGenerator, apiClient) {
this.scraper = scraper; this.scraper = scraper;
this.affiliateGenerator = affiliateGenerator; this.affiliateGenerator = affiliateGenerator;
this.apiClient = apiClient; this.apiClient = apiClient;
} }
async executar() { async executar() {
console.log("Iniciando processamento das promoções..."); console.log("Iniciando processamento das promoções...");
const dadosBrutos = await this.scraper.extrairDados(); const dadosBrutos = await this.scraper.extrairDados();
const dadosComAfiliacao = await this.affiliateGenerator.converterLista(dadosBrutos); const dadosComAfiliacao = await this.affiliateGenerator.converterLista(dadosBrutos);
let enviados = 0; let enviados = 0;
for (const dado of dadosComAfiliacao) { for (const dado of dadosComAfiliacao) {
const produto = new ProductEntity(dado); const produto = new ProductEntity(dado);
if (produto.isValid()) { if (produto.isValid()) {
await this.apiClient.enviarProduto(produto); await this.apiClient.enviarProduto(produto);
enviados++; enviados++;
} }
} }
console.log(`Processo finalizado! ${enviados} produtos foram enviados para a API.`); console.log(`Processo finalizado! ${enviados} produtos foram enviados para a API.`);
} }
} }
module.exports = ProcessPromotionsUseCase; module.exports = ProcessPromotionsUseCase;

View File

@@ -1,17 +1,17 @@
class ProductEntity { class ProductEntity {
constructor({ nomeProduto, precoOriginal, precoPromocao, porcetagemPromo, imagem, linkProduto }) { constructor({ nomeProduto, precoOriginal, precoPromocao, porcetagemPromo, imagem, linkProduto }) {
this.nomeProduto = nomeProduto || 'sem título'; this.nomeProduto = nomeProduto || 'sem título';
this.precoOriginal = precoOriginal || 0; this.precoOriginal = precoOriginal || 0;
this.precoPromocao = precoPromocao || 0; this.precoPromocao = precoPromocao || 0;
this.porcetagemPromo = porcetagemPromo || 0; this.porcetagemPromo = porcetagemPromo || 0;
this.imagem = imagem || ''; this.imagem = imagem || '';
this.linkProduto = linkProduto || ''; this.linkProduto = linkProduto || '';
this.plataforma = 'Mercado Livre'; // Regra de negócio fixa para este robô this.plataforma = 'Mercado Livre'; // Regra de negócio fixa para este robô
} }
isValid() { isValid() {
return this.nomeProduto !== 'Sem título' && this.precoPromocao > 0; return this.nomeProduto !== 'Sem título' && this.precoPromocao > 0;
} }
} }
module.exports = ProductEntity; module.exports = ProductEntity;

View File

@@ -1,17 +1,17 @@
class Produto { class Produto {
constructor({ nomeProduto, precoOriginal, precoPromocao, porcentagemPromo, imagem, linkProduto }) { constructor({ nomeProduto, precoOriginal, precoPromocao, porcentagemPromo, imagem, linkProduto }) {
this.nomeProduto = nomeProduto; this.nomeProduto = nomeProduto;
this.precoOriginal = precoOriginal; this.precoOriginal = precoOriginal;
this.precoPromocao = precoPromocao; this.precoPromocao = precoPromocao;
this.porcentagemPromo = porcentagemPromo; this.porcentagemPromo = porcentagemPromo;
this.imagem = imagem; this.imagem = imagem;
this.linkProduto = linkProduto; this.linkProduto = linkProduto;
this.plataforma = "Mercado Livre"; this.plataforma = "Mercado Livre";
} }
// Exemplo de Clean Code: médoto com resposabilidade única para validação // Exemplo de Clean Code: médoto com resposabilidade única para validação
isValido() { isValido() {
return this.nomeProduto !== 'sem título' && this.precoPromocao > 0; return this.nomeProduto !== 'sem título' && this.precoPromocao > 0;
} }
} }
module.exports = Produto; module.exports = Produto;

View File

@@ -1,15 +1,15 @@
const MercadoLivreScraper = require('./infrastructure/MercadoLivreScraper'); const MercadoLivreScraper = require('./infrastructure/MercadoLivreScraper');
const AffiliateGenerator = require('./infrastructure/AffiliateGenerator'); const AffiliateGenerator = require('./infrastructure/AffiliateGenerator');
const ApiClient = require('./infrastructure/ApiClient'); const ApiClient = require('./infrastructure/ApiClient');
const ProcessPromotionsUseCase = require('./application/ProcessPromotionsUseCase'); const ProcessPromotionsUseCase = require('./application/ProcessPromotionsUseCase');
// 1. Criamos as instâncias de cada ferramenta // 1. Criamos as instâncias de cada ferramenta
const scraper = new MercadoLivreScraper(); const scraper = new MercadoLivreScraper();
const affiliateGenerator = new AffiliateGenerator(); const affiliateGenerator = new AffiliateGenerator();
const apiClient = new ApiClient(); const apiClient = new ApiClient();
// 2. Entregamos todas as ferramentas para o Caso de Uso trabalhar // 2. Entregamos todas as ferramentas para o Caso de Uso trabalhar
const useCase = new ProcessPromotionsUseCase(scraper, affiliateGenerator, apiClient); const useCase = new ProcessPromotionsUseCase(scraper, affiliateGenerator, apiClient);
// 3. Damos o play no robô // 3. Damos o play no robô
useCase.executar(); useCase.executar();

View File

@@ -1,80 +1,80 @@
const puppeteer = require('puppeteer-extra'); const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth'); const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin()); puppeteer.use(StealthPlugin());
const fs = require('fs'); const fs = require('fs');
class AffiliateGenerator { class AffiliateGenerator {
async converterLista(produtosBrutos) { async converterLista(produtosBrutos) {
console.log('\nIniciando conversão para links de afiliado (Modo Docker/Invisível)...'); console.log('\nIniciando conversão para links de afiliado (Modo Docker/Invisível)...');
const browser = await puppeteer.launch({ const browser = await puppeteer.launch({
headless: false, headless: false,
args: [ args: [
'--no-sandbox', '--no-sandbox',
'--disable-setuid-sandbox', '--disable-setuid-sandbox',
'--disable-dev-shm-usage', '--disable-dev-shm-usage',
'--disable-blink-features=AutomationControlled', '--disable-blink-features=AutomationControlled',
'--disable-gpu', '--disable-gpu',
'--window-size=1920,1080' '--window-size=1920,1080'
] ]
}); });
const page = await browser.newPage(); const page = await browser.newPage();
// 1. Aquecimento: Acessa a home primeiro para gerar tokens de sessão naturais // 1. Aquecimento: Acessa a home primeiro para gerar tokens de sessão naturais
await page.goto('https://www.mercadolivre.com.br', { waitUntil: 'domcontentloaded' }); await page.goto('https://www.mercadolivre.com.br', { waitUntil: 'domcontentloaded' });
// 2. Injeta os cookies de autorização // 2. Injeta os cookies de autorização
if (fs.existsSync('./cookies.json')) { if (fs.existsSync('./cookies.json')) {
const cookies = JSON.parse(fs.readFileSync('./cookies.json', 'utf8')); const cookies = JSON.parse(fs.readFileSync('./cookies.json', 'utf8'));
await page.setCookie(...cookies); await page.setCookie(...cookies);
} }
// 3. Acessa o gerador com a sessão já "aquecida" // 3. Acessa o gerador com a sessão já "aquecida"
await page.goto('https://www.mercadolivre.com.br/afiliados/gerador', { waitUntil: 'networkidle2' }); await page.goto('https://www.mercadolivre.com.br/afiliados/gerador', { waitUntil: 'networkidle2' });
try { try {
await page.waitForSelector('textarea#url-0', { timeout: 15000 }); await page.waitForSelector('textarea#url-0', { timeout: 15000 });
} catch (erro) { } catch (erro) {
console.log('\n❌ ERRO CRÍTICO: Bloqueio detectado ou cookies inválidos.'); console.log('\n❌ ERRO CRÍTICO: Bloqueio detectado ou cookies inválidos.');
await browser.close(); await browser.close();
return []; // Retorna array vazio para não quebrar a API com Erro 500 return []; // Retorna array vazio para não quebrar a API com Erro 500
} }
const produtosConvertidos = []; const produtosConvertidos = [];
for (const produto of produtosBrutos) { for (const produto of produtosBrutos) {
try { try {
await page.evaluate(() => { document.querySelector('textarea#url-0').value = ''; }); await page.evaluate(() => { document.querySelector('textarea#url-0').value = ''; });
await page.type('textarea#url-0', produto.linkProduto); await page.type('textarea#url-0', produto.linkProduto);
await page.evaluate(() => { await page.evaluate(() => {
const spans = Array.from(document.querySelectorAll('span.andes-button__content')); const spans = Array.from(document.querySelectorAll('span.andes-button__content'));
const btnGerar = spans.find(s => s.textContent.trim() === 'Gerar'); const btnGerar = spans.find(s => s.textContent.trim() === 'Gerar');
if (btnGerar && btnGerar.parentElement) btnGerar.parentElement.click(); if (btnGerar && btnGerar.parentElement) btnGerar.parentElement.click();
}); });
await new Promise(r => setTimeout(r, 2000)); // Tempo extra para o ML processar await new Promise(r => setTimeout(r, 2000)); // Tempo extra para o ML processar
const linkAfiliado = await page.evaluate(() => { const linkAfiliado = await page.evaluate(() => {
const textareas = document.querySelectorAll('textarea[id^="textfield-copyLink"]'); const textareas = document.querySelectorAll('textarea[id^="textfield-copyLink"]');
return textareas.length > 0 ? textareas[textareas.length - 1].value : null; return textareas.length > 0 ? textareas[textareas.length - 1].value : null;
}); });
if (linkAfiliado && linkAfiliado.includes('meli.la')) { if (linkAfiliado && linkAfiliado.includes('meli.la')) {
produto.linkProduto = linkAfiliado; produto.linkProduto = linkAfiliado;
produtosConvertidos.push(produto); // Só adiciona se a conversão foi um sucesso produtosConvertidos.push(produto); // Só adiciona se a conversão foi um sucesso
console.log(`✅ Convertido: ${produto.nomeProduto.substring(0, 30)}...`); console.log(`✅ Convertido: ${produto.nomeProduto.substring(0, 30)}...`);
} else { } else {
throw new Error("Link inválido ou não gerado."); throw new Error("Link inválido ou não gerado.");
} }
} catch (error) { } catch (error) {
console.log(`⚠️ Descartado (Falha na conversão): ${produto.nomeProduto.substring(0, 30)}...`); console.log(`⚠️ Descartado (Falha na conversão): ${produto.nomeProduto.substring(0, 30)}...`);
} }
} }
await browser.close(); await browser.close();
return produtosConvertidos; return produtosConvertidos;
} }
} }
module.exports = AffiliateGenerator; module.exports = AffiliateGenerator;

View File

@@ -1,27 +1,27 @@
const axios = require('axios'); const axios = require('axios');
class ApiClient { class ApiClient {
constructor(baseURL) { constructor(baseURL) {
// Forçando a URL real da sua API, ignorando o localhost // Forçando a URL real da sua API, ignorando o localhost
this.urlDestino = 'https://duck-api.stackpanel.com.br/duck-api/produto/salvar'; this.urlDestino = 'https://duck-api.stackpanel.com.br/duck-api/produto/salvar';
} }
async enviarProduto(produto) { async enviarProduto(produto) {
// Montando o body EXATAMENTE no formato que a sua API exige // Montando o body EXATAMENTE no formato que a sua API exige
const payload = { const payload = {
nomeProduto: produto.nomeProduto, nomeProduto: produto.nomeProduto,
descricaoProduto: `Desconto de ${produto.porcentagemPromo}%. Link da oferta: ${produto.linkProduto}`, descricaoProduto: `Desconto de ${produto.porcentagemPromo}%. Link da oferta: ${produto.linkProduto}`,
precoProdutoAtualizado: produto.precoPromocao, precoProdutoAtualizado: produto.precoPromocao,
nomePlataforma: "Mercado Livre" nomePlataforma: "Mercado Livre"
}; };
try { try {
await axios.post(this.urlDestino, payload); await axios.post(this.urlDestino, payload);
console.log(`✅ Enviado com sucesso: ${produto.nomeProduto}`); console.log(`✅ Enviado com sucesso: ${produto.nomeProduto}`);
} catch (erro) { } catch (erro) {
console.log(`❌ Erro ao enviar ${produto.nomeProduto} | Motivo: ${erro.message}`); console.log(`❌ Erro ao enviar ${produto.nomeProduto} | Motivo: ${erro.message}`);
} }
} }
} }
module.exports = ApiClient; module.exports = ApiClient;

View File

@@ -1,86 +1,86 @@
const puppeteer = require('puppeteer-extra'); const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth'); const StealthPlugin = require('puppeteer-extra-plugin-stealth');
// Ativa o modo de camuflagem para evitar detecção // Ativa o modo de camuflagem para evitar detecção
puppeteer.use(StealthPlugin()); puppeteer.use(StealthPlugin());
class MercadoLivreScraper { class MercadoLivreScraper {
async extrairDados() { async extrairDados() {
console.log('Iniciando extração de promoções do Mercado Livre...'); console.log('Iniciando extração de promoções do Mercado Livre...');
const browser = await puppeteer.launch({ const browser = await puppeteer.launch({
headless: true, headless: true,
args: [ args: [
'--no-sandbox', '--no-sandbox',
'--disable-setuid-sandbox', '--disable-setuid-sandbox',
'--disable-blink-features=AutomationControlled' '--disable-blink-features=AutomationControlled'
] ]
}); });
const page = await browser.newPage(); const page = await browser.newPage();
// Define um User-Agent e resolução de tela de um usuário real // Define um User-Agent e resolução de tela de um usuário real
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'); await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
await page.setViewport({ width: 1366, height: 768 }); await page.setViewport({ width: 1366, height: 768 });
try { try {
await page.goto('https://www.mercadolivre.com.br/ofertas', { waitUntil: 'networkidle2' }); await page.goto('https://www.mercadolivre.com.br/ofertas', { waitUntil: 'networkidle2' });
await page.waitForSelector('.poly-card', { timeout: 15000 }); await page.waitForSelector('.poly-card', { timeout: 15000 });
const produtos = await page.evaluate(() => { const produtos = await page.evaluate(() => {
const itens = document.querySelectorAll('.poly-card'); const itens = document.querySelectorAll('.poly-card');
const resultados = []; const resultados = [];
itens.forEach(item => { itens.forEach(item => {
const elementoTitulo = item.querySelector('.poly-component__title') || item.querySelector('h2'); const elementoTitulo = item.querySelector('.poly-component__title') || item.querySelector('h2');
const nomeProduto = elementoTitulo ? elementoTitulo.innerText.trim() : 'Sem título'; const nomeProduto = elementoTitulo ? elementoTitulo.innerText.trim() : 'Sem título';
const elementoMarca = item.querySelector('.poly-component__brand'); const elementoMarca = item.querySelector('.poly-component__brand');
const marca = elementoMarca ? elementoMarca.innerText.trim() : ''; const marca = elementoMarca ? elementoMarca.innerText.trim() : '';
const tituloFinal = marca ? `${marca} - ${nomeProduto}` : nomeProduto; const tituloFinal = marca ? `${marca} - ${nomeProduto}` : nomeProduto;
const elementoLink = item.querySelector('a'); const elementoLink = item.querySelector('a');
const linkProduto = elementoLink ? elementoLink.href : 'Sem link'; const linkProduto = elementoLink ? elementoLink.href : 'Sem link';
const elementoImagem = item.querySelector('img'); const elementoImagem = item.querySelector('img');
const imagem = elementoImagem ? (elementoImagem.getAttribute('data-src') || elementoImagem.src) : 'Sem imagem'; const imagem = elementoImagem ? (elementoImagem.getAttribute('data-src') || elementoImagem.src) : 'Sem imagem';
const elementoPreco = item.querySelector('.andes-money-amount__fraction'); const elementoPreco = item.querySelector('.andes-money-amount__fraction');
const precoPromocao = elementoPreco ? parseFloat(elementoPreco.innerText.replace('.', '').replace(',', '.')) : 0; const precoPromocao = elementoPreco ? parseFloat(elementoPreco.innerText.replace('.', '').replace(',', '.')) : 0;
const elementoPrecoOriginal = item.querySelector('s .andes-money-amount__fraction') || item.querySelector('.poly-price__old .andes-money-amount__fraction'); const elementoPrecoOriginal = item.querySelector('s .andes-money-amount__fraction') || item.querySelector('.poly-price__old .andes-money-amount__fraction');
const precoOriginal = elementoPrecoOriginal ? parseFloat(elementoPrecoOriginal.innerText.replace('.', '').replace(',', '.')) : precoPromocao; const precoOriginal = elementoPrecoOriginal ? parseFloat(elementoPrecoOriginal.innerText.replace('.', '').replace(',', '.')) : precoPromocao;
let porcentagemPromo = 0; let porcentagemPromo = 0;
if (precoOriginal > 0 && precoOriginal > precoPromocao) { if (precoOriginal > 0 && precoOriginal > precoPromocao) {
porcentagemPromo = Math.round(((precoOriginal - precoPromocao) / precoOriginal) * 100); porcentagemPromo = Math.round(((precoOriginal - precoPromocao) / precoOriginal) * 100);
} }
resultados.push({ resultados.push({
nomeProduto: tituloFinal, nomeProduto: tituloFinal,
linkProduto: linkProduto, linkProduto: linkProduto,
precoOriginal: precoOriginal, precoOriginal: precoOriginal,
precoPromocao: precoPromocao, precoPromocao: precoPromocao,
porcentagemPromo: porcentagemPromo, porcentagemPromo: porcentagemPromo,
imagem: imagem imagem: imagem
}); });
}); });
return resultados; return resultados;
}); });
console.log(`\n--- DIAGNÓSTICO FINAL ---`); console.log(`\n--- DIAGNÓSTICO FINAL ---`);
console.log(`O robô enxergou ${produtos.length} cartões na página em modo invisível.`); console.log(`O robô enxergou ${produtos.length} cartões na página em modo invisível.`);
console.log(`-------------------------\n`); console.log(`-------------------------\n`);
await browser.close(); await browser.close();
return produtos; return produtos;
} catch (error) { } catch (error) {
console.error('Erro durante a extração:', error.message); console.error('Erro durante a extração:', error.message);
await browser.close(); await browser.close();
return []; return [];
} }
} }
} }
module.exports = MercadoLivreScraper; module.exports = MercadoLivreScraper;