/** * Collecteur Inforgreffe/Societe.com - Informations entreprises * Scraping léger avec politesse pour récupérer les données d'entreprises */ const fetch = require('node-fetch'); const { JSDOM } = require('jsdom'); const SOCIETE_COM_BASE_URL = 'https://www.societe.com'; const INFORGREFFE_BASE_URL = 'https://www.inforgreffe.com'; const USER_AGENT = '4NK-IA-Front/1.0 (Document Analysis Tool)'; const REQUEST_DELAY_MS = 1500; // 1.5 secondes entre les requêtes const REQUEST_TIMEOUT_MS = 12000; // 12 secondes timeout /** * Recherche une entreprise sur Societe.com et Inforgreffe * @param {string} companyName - Nom de l'entreprise * @param {string} siren - SIREN (optionnel) * @returns {Promise} Résultat de la recherche */ async function searchCompanyInfo(companyName, siren = '') { const startTime = Date.now(); try { console.log(`[Inforgreffe] Recherche entreprise: ${companyName} ${siren ? `(SIREN: ${siren})` : ''}`); // Recherche sur Societe.com d'abord (plus accessible) const societeComResult = await searchSocieteCom(companyName, siren); // Délai de politesse await new Promise(resolve => setTimeout(resolve, REQUEST_DELAY_MS)); // Recherche sur Inforgreffe si SIREN disponible let inforgreffeResult = null; if (siren) { inforgreffeResult = await searchInforgreffe(siren); await new Promise(resolve => setTimeout(resolve, REQUEST_DELAY_MS)); } // Fusion des résultats const mergedResult = mergeCompanyResults(societeComResult, inforgreffeResult, companyName); const duration = Date.now() - startTime; console.log(`[Inforgreffe] Recherche terminée en ${duration}ms`); return { success: true, duration, company: mergedResult, sources: { societeCom: societeComResult, inforgreffe: inforgreffeResult }, timestamp: new Date().toISOString() }; } catch (error) { const duration = Date.now() - startTime; console.error(`[Inforgreffe] Erreur recherche:`, error.message); return { success: false, duration, error: error.message, company: null, sources: {}, timestamp: new Date().toISOString() }; } } /** * Recherche sur Societe.com * @param {string} companyName - Nom de l'entreprise * @param {string} siren - SIREN (optionnel) * @returns {Promise} Résultat Societe.com */ async function searchSocieteCom(companyName, siren = '') { try { // Construction de l'URL de recherche const searchQuery = siren || companyName; const searchUrl = `${SOCIETE_COM_BASE_URL}/search?q=${encodeURIComponent(searchQuery)}`; const response = await fetch(searchUrl, { method: 'GET', headers: { 'User-Agent': USER_AGENT, 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'fr-FR,fr;q=0.9,en;q=0.8', 'Accept-Encoding': 'gzip, deflate, br', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1' }, timeout: REQUEST_TIMEOUT_MS }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const html = await response.text(); const dom = new JSDOM(html); const document = dom.window.document; return extractSocieteComData(document, companyName, siren); } catch (error) { console.warn(`[Societe.com] Erreur:`, error.message); return { success: false, error: error.message, data: null }; } } /** * Recherche sur Inforgreffe * @param {string} siren - SIREN de l'entreprise * @returns {Promise} Résultat Inforgreffe */ async function searchInforgreffe(siren) { try { // URL de recherche par SIREN const searchUrl = `${INFORGREFFE_BASE_URL}/entreprise/${siren}`; const response = await fetch(searchUrl, { method: 'GET', headers: { 'User-Agent': USER_AGENT, 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'fr-FR,fr;q=0.9,en;q=0.8', 'Accept-Encoding': 'gzip, deflate, br', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1' }, timeout: REQUEST_TIMEOUT_MS }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const html = await response.text(); const dom = new JSDOM(html); const document = dom.window.document; return extractInforgreffeData(document, siren); } catch (error) { console.warn(`[Inforgreffe] Erreur:`, error.message); return { success: false, error: error.message, data: null }; } } /** * Extrait les données de Societe.com * @param {Document} document - Document DOM parsé * @param {string} companyName - Nom de l'entreprise * @param {string} siren - SIREN * @returns {Object} Données extraites */ function extractSocieteComData(document, companyName, siren) { try { const data = { name: companyName, siren: siren, siret: '', forme: '', capital: '', adresse: '', dirigeants: [], activite: '', dateCreation: '', sourceUrl: SOCIETE_COM_BASE_URL }; // Extraction du nom de l'entreprise const nameElement = document.querySelector('.company-name, .nom-entreprise, h1, .title'); if (nameElement) { data.name = nameElement.textContent.trim(); } // Extraction du SIREN/SIRET const sirenElement = document.querySelector('.siren, .num-siren, [data-siren]'); if (sirenElement) { const sirenText = sirenElement.textContent || sirenElement.getAttribute('data-siren'); data.siren = extractSiren(sirenText); } // Extraction de la forme juridique const formeElement = document.querySelector('.forme, .forme-juridique, .legal-form'); if (formeElement) { data.forme = formeElement.textContent.trim(); } // Extraction du capital const capitalElement = document.querySelector('.capital, .capital-social, .share-capital'); if (capitalElement) { data.capital = capitalElement.textContent.trim(); } // Extraction de l'adresse const adresseElement = document.querySelector('.adresse, .address, .company-address'); if (adresseElement) { data.adresse = adresseElement.textContent.trim(); } // Extraction des dirigeants const dirigeantsElements = document.querySelectorAll('.dirigeant, .manager, .president, .gérant'); dirigeantsElements.forEach(element => { const name = element.textContent.trim(); if (name && name.length > 2) { data.dirigeants.push({ nom: name, fonction: 'Dirigeant', source: 'societe.com' }); } }); // Extraction de l'activité const activiteElement = document.querySelector('.activite, .activity, .secteur'); if (activiteElement) { data.activite = activiteElement.textContent.trim(); } // Extraction de la date de création const dateElement = document.querySelector('.date-creation, .creation-date, .date-creation-entreprise'); if (dateElement) { data.dateCreation = dateElement.textContent.trim(); } return { success: true, data }; } catch (error) { console.warn(`[Societe.com] Erreur extraction:`, error.message); return { success: false, error: error.message, data: null }; } } /** * Extrait les données d'Inforgreffe * @param {Document} document - Document DOM parsé * @param {string} siren - SIREN * @returns {Object} Données extraites */ function extractInforgreffeData(document, siren) { try { const data = { siren: siren, name: '', siret: '', forme: '', capital: '', adresse: '', dirigeants: [], activite: '', dateCreation: '', sourceUrl: `${INFORGREFFE_BASE_URL}/entreprise/${siren}` }; // Extraction du nom de l'entreprise const nameElement = document.querySelector('.company-name, .nom-entreprise, h1, .title'); if (nameElement) { data.name = nameElement.textContent.trim(); } // Extraction du SIRET const siretElement = document.querySelector('.siret, .num-siret, [data-siret]'); if (siretElement) { data.siret = siretElement.textContent.trim(); } // Extraction de la forme juridique const formeElement = document.querySelector('.forme, .forme-juridique, .legal-form'); if (formeElement) { data.forme = formeElement.textContent.trim(); } // Extraction du capital const capitalElement = document.querySelector('.capital, .capital-social, .share-capital'); if (capitalElement) { data.capital = capitalElement.textContent.trim(); } // Extraction de l'adresse const adresseElement = document.querySelector('.adresse, .address, .company-address'); if (adresseElement) { data.adresse = adresseElement.textContent.trim(); } // Extraction des dirigeants const dirigeantsElements = document.querySelectorAll('.dirigeant, .manager, .president, .gérant'); dirigeantsElements.forEach(element => { const name = element.textContent.trim(); if (name && name.length > 2) { data.dirigeants.push({ nom: name, fonction: 'Dirigeant', source: 'inforgreffe.com' }); } }); return { success: true, data }; } catch (error) { console.warn(`[Inforgreffe] Erreur extraction:`, error.message); return { success: false, error: error.message, data: null }; } } /** * Fusionne les résultats de Societe.com et Inforgreffe * @param {Object} societeComResult - Résultat Societe.com * @param {Object} inforgreffeResult - Résultat Inforgreffe * @param {string} originalName - Nom original de l'entreprise * @returns {Object} Données fusionnées */ function mergeCompanyResults(societeComResult, inforgreffeResult, originalName) { const merged = { name: originalName, siren: '', siret: '', forme: '', capital: '', adresse: '', dirigeants: [], activite: '', dateCreation: '', sources: [] }; // Fusion des données Societe.com if (societeComResult.success && societeComResult.data) { const sc = societeComResult.data; merged.name = sc.name || merged.name; merged.siren = sc.siren || merged.siren; merged.siret = sc.siret || merged.siret; merged.forme = sc.forme || merged.forme; merged.capital = sc.capital || merged.capital; merged.adresse = sc.adresse || merged.adresse; merged.activite = sc.activite || merged.activite; merged.dateCreation = sc.dateCreation || merged.dateCreation; merged.dirigeants.push(...sc.dirigeants); merged.sources.push('societe.com'); } // Fusion des données Inforgreffe if (inforgreffeResult && inforgreffeResult.success && inforgreffeResult.data) { const ig = inforgreffeResult.data; merged.name = ig.name || merged.name; merged.siren = ig.siren || merged.siren; merged.siret = ig.siret || merged.siret; merged.forme = ig.forme || merged.forme; merged.capital = ig.capital || merged.capital; merged.adresse = ig.adresse || merged.adresse; merged.dateCreation = ig.dateCreation || merged.dateCreation; merged.dirigeants.push(...ig.dirigeants); merged.sources.push('inforgreffe.com'); } // Déduplication des dirigeants merged.dirigeants = merged.dirigeants.filter((dirigeant, index, self) => index === self.findIndex(d => d.nom === dirigeant.nom) ); return merged; } /** * Extrait le SIREN d'un texte * @param {string} text - Texte contenant potentiellement un SIREN * @returns {string} SIREN extrait */ function extractSiren(text) { if (!text) return ''; // Recherche d'un SIREN (9 chiffres) const sirenMatch = text.match(/\b(\d{9})\b/); return sirenMatch ? sirenMatch[1] : ''; } /** * Génère un résumé des données d'entreprise pour le PDF * @param {Object} companyData - Données de l'entreprise * @returns {Object} Résumé formaté */ function generateCompanySummary(companyData) { return { name: companyData.name, siren: companyData.siren, siret: companyData.siret, forme: companyData.forme, capital: companyData.capital, adresse: companyData.adresse, dirigeantsCount: companyData.dirigeants.length, activite: companyData.activite, dateCreation: companyData.dateCreation, sources: companyData.sources, hasCompleteInfo: !!(companyData.siren && companyData.forme && companyData.adresse), summary: companyData.siren ? `Entreprise trouvée: ${companyData.name} (SIREN: ${companyData.siren})` : `Informations partielles pour: ${companyData.name}` }; } module.exports = { searchCompanyInfo, generateCompanySummary };