feat: complete N8N scrapers for 6 sources and seed script

- Create comprehensive N8N workflow for all 6 parapharmacy sources
- Create webhook-triggered manual scraping workflow
- Add seed script with 20 sample products
- Update n8n/README.md with complete documentation
- Update docs/parapharmacy.md with seeding instructions
- Add seed script to package.json

Sources configured:
- Promofarma
- Pharmarket
- DocMorris
- 1001Farma
- Primor
- MiFarma
This commit is contained in:
Antoni Nuñez Romeu
2026-07-16 13:00:20 +02:00
parent cb564fb170
commit 30f97fe87d
7 changed files with 973 additions and 68 deletions
+111
View File
@@ -0,0 +1,111 @@
import axios from 'axios';
const API_URL = process.env.PARAPHARMACY_API_URL || 'http://localhost:3002';
// Common parapharmacy search queries
const QUERIES = [
'crema hidratante',
'protector solar',
'champú',
'gel ducha',
'vitaminas',
'paracetamol',
'ibuprofeno',
'omeprazol',
'spray nasal',
'gotas para los ojos',
'crema manos',
'bálsamo labial',
'antiséptico',
'vendas',
'termómetro'
];
// Simulated products for testing (replace with real scraping)
const MOCK_PRODUCTS = [
{ name: 'Bioderma Atoderm Crema Hidratante 500ml', brand: 'Bioderma', category: 'Dermocosmética', price: 18.95 },
{ name: 'La Roche-Posay Anthelios Airlicium FPS50+', brand: 'La Roche-Posay', category: 'Solar', price: 19.95 },
{ name: 'Mustela Gel de Ducha 500ml', brand: 'Mustela', category: 'Bebé', price: 12.50 },
{ name: 'Centrum Multivitaminicos 30 comprimidos', brand: 'Centrum', category: 'Vitaminas', price: 15.80 },
{ name: 'Dolocordalpan 1g Paracetamol 20 sobres', brand: 'Dolocordalpan', category: 'Analgésicos', price: 4.95 },
{ name: 'Nurofen Flash 400mg 20 cápsulas', brand: 'Nurofen', category: 'Antiinflamatorios', price: 6.75 },
{ name: 'Omeprazol Cinfa 20mg 28 cápsulas', brand: 'Cinfa', category: 'Gastrointestinal', price: 8.50 },
{ name: 'Vichy Mineral 89 Sérum Hidratante 30ml', brand: 'Vichy', category: 'Dermocosmética', price: 25.90 },
{ name: 'Avène Agua Termal 300ml', brand: 'Avène', category: 'Dermocosmética', price: 9.95 },
{ name: 'CeraVe Crema Hidratante 340g', brand: 'CeraVe', category: 'Dermocosmética', price: 14.95 },
{ name: 'Ibuprofeno Alter 600mg 20 comprimidos', brand: 'Alter', category: 'Antiinflamatorios', price: 5.20 },
{ name: 'Salonpas Parches Analgésicos 5 unidades', brand: 'Salonpas', category: 'Analgésicos', price: 7.80 },
{ name: 'Fisiomer Spray Nasal 135ml', brand: 'Fisiomer', category: 'Respiratorio', price: 11.50 },
{ name: 'Thealoz Duo Colirio 10ml', brand: 'Thea', category: 'Oftalmología', price: 12.95 },
{ name: 'Neutrogena Crema Manos Noruega 50ml', brand: 'Neutrogena', category: 'Dermocosmética', price: 4.50 },
{ name: 'Capricare 1 Leche en polvo 400g', brand: 'Capricare', category: 'Fórmulas lácteas', price: 14.95 },
{ name: 'Nutribén 2 Leche 800g', brand: 'Nutribén', category: 'Fórmulas lácteas', price: 16.50 },
{ name: 'Bebelin Vitamina C 1g 20 comprimidos', brand: 'Bebelín', category: 'Vitaminas', price: 6.95 },
{ name: 'Lacer Pasta Dientes Sensibilidad 75ml', brand: 'Lacer', category: 'Oral', price: 5.80 },
{ name: 'Fotosan Crema Solar FPS50 200ml', brand: 'Fotosan', category: 'Solar', price: 22.50 }
];
async function checkAPIHealth() {
try {
const response = await axios.get(`${API_URL}/api/health`);
return response.data.status === 'ok';
} catch (error) {
return false;
}
}
async function seedProducts() {
console.log('🌱 Starting parapharmacy database seed...\n');
// Check if API is running
const apiHealthy = await checkAPIHealth();
if (!apiHealthy) {
console.error('❌ Parapharmacy API is not running. Start it with: npm run dev');
process.exit(1);
}
console.log('✅ Parapharmacy API is running\n');
// Seed products
let created = 0;
let updated = 0;
let errors = 0;
for (const product of MOCK_PRODUCTS) {
try {
const productData = {
...product,
source: 'seed',
source_product_id: `seed_${product.name.toLowerCase().replace(/[^a-z0-9]/g, '_')}`,
source_url: `https://www.promofarma.com/es/search?q=${encodeURIComponent(product.name)}`,
image_url: null,
currency: 'EUR',
available: true,
scraped_at: new Date().toISOString()
};
await axios.post(`${API_URL}/api/products`, productData);
created++;
console.log(` ✅ Created: ${product.name}`);
} catch (error) {
if (error.response?.status === 409) {
updated++;
console.log(` ⏭️ Already exists: ${product.name}`);
} else {
errors++;
console.error(` ❌ Error: ${product.name} - ${error.message}`);
}
}
}
console.log('\n📊 Seed Summary:');
console.log(` Created: ${created}`);
console.log(` Already exists: ${updated}`);
console.log(` Errors: ${errors}`);
console.log(` Total: ${MOCK_PRODUCTS.length}`);
}
// Run seed
seedProducts().catch(error => {
console.error('❌ Seed failed:', error.message);
process.exit(1);
});