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]
Icon=folder-games
[Desktop Entry]
Icon=folder-games

284
.gitignore vendored
View File

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

3102
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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