From 32f39d1086d50a0c4e89b2faecfacc8cfc548fbb Mon Sep 17 00:00:00 2001 From: jfgiraud Date: Wed, 19 Aug 2026 15:46:41 +0000 Subject: [PATCH] import cac40,ibex35 et dowjones --- import_dowjones.php | 1500 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1500 insertions(+) create mode 100644 import_dowjones.php diff --git a/import_dowjones.php b/import_dowjones.php new file mode 100644 index 0000000..5e6b6f2 --- /dev/null +++ b/import_dowjones.php @@ -0,0 +1,1500 @@ + MySQL GF + * + * Source : + * https://www.investing.com/equities/americas + * + * Référentiel : + * indice + * + * Données : + * cours_actions + * + * IMPORTANT : + * + * - NE MODIFIE JAMAIS la table indice + * - nom / ISIN / symbole viennent de indice + * - la correspondance se fait sur le NOM + * - "derived" est supprimé du nom Investing + * + * ============================================================ + */ + +$dbHost = 'localhost'; +$dbName = 'GF'; +$dbUser = 'root'; +$dbPass = 'sysadm-1963'; + +$url = 'https://www.investing.com/equities/americas'; + +$logPrefix = date('Y-m-d H:i:s'); + +echo "$logPrefix - Début import DOWJONES\n"; + + +/* + * ============================================================ + * MYSQL + * ============================================================ + */ + +try { + + $pdo = new PDO( + "mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", + $dbUser, + $dbPass, + [ + PDO::ATTR_ERRMODE => + PDO::ERRMODE_EXCEPTION, + + PDO::ATTR_DEFAULT_FETCH_MODE => + PDO::FETCH_ASSOC, + + PDO::ATTR_EMULATE_PREPARES => + false + ] + ); +} catch (PDOException $e) { + + die("$logPrefix - ERREUR MYSQL : " . + $e->getMessage() . + PHP_EOL); +} + + +/* + * ============================================================ + * CHARGEMENT DU REFERENTIEL + * + * On récupère uniquement les valeurs actives. + * + * Alphabet pourra être présent dans indice avec : + * + * actif = 0 + * + * tant que le changement n'est pas effectif. + * + * ============================================================ + */ + +$sqlIndice = " + + SELECT + nom, + isin, + symbole + + FROM indice + + WHERE indice = 'DOWJONES' + + AND actif = 1 + +"; + + +$stmtIndice = $pdo->query($sqlIndice); + +$referentiel = []; + + +while ($row = $stmtIndice->fetch()) { + + $cle = normalizeName( + $row['nom'] + ); + + $referentiel[$cle] = [ + + 'nom' => + $row['nom'], + + 'isin' => + $row['isin'], + + 'symbole' => + $row['symbole'] + + ]; +} + + +echo +"$logPrefix - Référentiel DOWJONES : " . + count($referentiel) . + " valeurs\n"; + + +if (count($referentiel) !== 30) { + + echo + "$logPrefix - ATTENTION : le référentiel " . + "DOWJONES contient " . + count($referentiel) . + " valeurs au lieu de 30.\n"; +} + + +/* + * ============================================================ + * CURL + * ============================================================ + * + * Investing peut répondre 403 selon le User-Agent. + * + * On utilise ici une requête proche d'un navigateur réel. + * + * ============================================================ + */ + +$ch = curl_init($url); + +curl_setopt_array($ch, [ + + CURLOPT_RETURNTRANSFER => + true, + + CURLOPT_FOLLOWLOCATION => + true, + + CURLOPT_MAXREDIRS => + 5, + + CURLOPT_CONNECTTIMEOUT => + 15, + + CURLOPT_TIMEOUT => + 30, + + CURLOPT_ENCODING => + '', + + CURLOPT_SSL_VERIFYPEER => + true, + + CURLOPT_SSL_VERIFYHOST => + 2, + + CURLOPT_USERAGENT => + 'Mozilla/5.0 (X11; Linux x86_64) ' . + 'AppleWebKit/537.36 ' . + '(KHTML, like Gecko) ' . + 'Chrome/139.0.0.0 Safari/537.36', + + CURLOPT_HTTPHEADER => [ + + 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', + + 'Accept-Language: en-US,en;q=0.9', + + 'Cache-Control: no-cache', + + 'Pragma: no-cache', + + 'Upgrade-Insecure-Requests: 1', + + 'Sec-Fetch-Dest: document', + + 'Sec-Fetch-Mode: navigate', + + 'Sec-Fetch-Site: none', + + 'Sec-Fetch-User: ?1' + + ] + +]); + + +$html = curl_exec($ch); + + +$httpCode = curl_getinfo( + $ch, + CURLINFO_HTTP_CODE +); + + +$curlError = curl_error($ch); + + +curl_close($ch); + + +/* + * ============================================================ + * VERIFICATION CURL + * ============================================================ + */ + +if ( + $html === false || + $html === '' +) { + + die("$logPrefix - ERREUR CURL : " . + $curlError . + PHP_EOL); +} + + +if ($httpCode !== 200) { + + file_put_contents( + '/tmp/investing_dowjones_error.html', + $html + ); + + die("$logPrefix - ERREUR HTTP : " . + $httpCode . + PHP_EOL); +} + + +echo +"$logPrefix - Page récupérée : " . + strlen($html) . + " octets\n"; + + +/* + * ============================================================ + * DOM + * ============================================================ + */ + +libxml_use_internal_errors(true); + +$dom = new DOMDocument(); + + +if (!$dom->loadHTML($html)) { + + die("$logPrefix - ERREUR : impossible de parser le HTML\n"); +} + + +libxml_clear_errors(); + + +$xpath = new DOMXPath($dom); + + +/* + * ============================================================ + * RECHERCHE DU TABLEAU + * + * Colonnes attendues : + * + * Name + * Last + * High + * Low + * Chg. + * Chg. % + * Vol. + * Time + * + * ============================================================ + */ + +$tables = $xpath->query('//table'); + +$targetTable = null; + + +foreach ($tables as $table) { + + $rowsTable = $xpath->query( + './/tr', + $table + ); + + + if ($rowsTable->length === 0) { + continue; + } + + + $firstRow = + $rowsTable->item(0); + + + $cells = $xpath->query( + './th|./td', + $firstRow + ); + + + $headers = []; + + + foreach ($cells as $cell) { + + $headers[] = cleanText( + $cell->textContent + ); + } + + + if ( + + in_array( + 'Name', + $headers, + true + ) + + && + + in_array( + 'Last', + $headers, + true + ) + + && + + in_array( + 'High', + $headers, + true + ) + + && + + in_array( + 'Low', + $headers, + true + ) + + && + + in_array( + 'Chg. %', + $headers, + true + ) + + && + + in_array( + 'Vol.', + $headers, + true + ) + + && + + in_array( + 'Time', + $headers, + true + ) + + ) { + + $targetTable = + $table; + + + echo + "$logPrefix - Tableau DOWJONES trouvé\n"; + + + echo + "$logPrefix - Colonnes : " . + implode( + '|', + $headers + ) . + "\n"; + + + break; + } +} + + +/* + * ============================================================ + * TABLEAU NON TROUVE + * ============================================================ + */ + +if ($targetTable === null) { + + file_put_contents( + '/tmp/investing_dowjones_error.html', + $html + ); + + + die("$logPrefix - ERREUR : tableau DOWJONES introuvable\n"); +} + + +/* + * ============================================================ + * COLONNES + * ============================================================ + */ + +$rows = $xpath->query( + './/tr', + $targetTable +); + + +$headerCells = $xpath->query( + './th|./td', + $rows->item(0) +); + + +$columns = []; + + +foreach ($headerCells as $index => $cell) { + + $name = cleanText( + $cell->textContent + ); + + + $columns[$name] = $index; +} + + +/* + * ============================================================ + * PREPARATION MYSQL + * ============================================================ + */ + +$sql = " + +INSERT INTO cours_actions +( + indice, + nom, + isin, + symbole, + cours, + plus_haut, + plus_bas, + variation, + variation_pct, + volume, + date_cours, + source +) + +VALUES +( + :indice, + :nom, + :isin, + :symbole, + :cours, + :plus_haut, + :plus_bas, + :variation, + :variation_pct, + :volume, + :date_cours, + :source +) + +ON DUPLICATE KEY UPDATE + + nom = + VALUES(nom), + + isin = + VALUES(isin), + + symbole = + VALUES(symbole), + + cours = + VALUES(cours), + + plus_haut = + VALUES(plus_haut), + + plus_bas = + VALUES(plus_bas), + + variation = + VALUES(variation), + + variation_pct = + VALUES(variation_pct), + + volume = + VALUES(volume), + + date_cours = + VALUES(date_cours), + + source = + VALUES(source) + +"; + + +$stmt = $pdo->prepare($sql); + + +/* + * ============================================================ + * DATE / HEURE + * + * MySQL utilise UTC. + * + * On demande explicitement à MySQL son heure UTC. + * + * ============================================================ + */ + +$stmtDate = $pdo->query( + "SELECT UTC_TIMESTAMP() AS utc_now" +); + + +$dateCours = + $stmtDate->fetch()['utc_now']; + + +$dateCours = + substr( + $dateCours, + 0, + 16 + ) . + ':00'; + + +echo +"$logPrefix - Date cours UTC : " . + $dateCours . + "\n"; + + +/* + * ============================================================ + * TRAITEMENT DES VALEURS + * ============================================================ + */ + +$count = 0; + +$errors = 0; + +$notFound = []; + + +/* + * ============================================================ + * LIGNE 0 = ENTETES + * ============================================================ + */ + +for ( + $r = 1; + $r < $rows->length; + $r++ +) { + + $row = + $rows->item($r); + + + $cells = $xpath->query( + './td', + $row + ); + + + if ($cells->length < 7) { + continue; + } + + + /* + * -------------------------------------------------------- + * VALEURS DES CELLULES + * -------------------------------------------------------- + */ + + $values = []; + + + foreach ($cells as $cell) { + + $values[] = + cleanText( + $cell->textContent + ); + } + + + /* + * -------------------------------------------------------- + * NOM INVESTING + * -------------------------------------------------------- + */ + + $nomInvesting = + $values[$columns['Name'] ?? 0] ?? ''; + + + if ($nomInvesting === '') { + continue; + } + + + /* + * -------------------------------------------------------- + * SUPPRESSION DE "derived" + * + * Exemples : + * + * ACS derived + * Apple derived + * + * devient : + * + * ACS + * Apple + * + * -------------------------------------------------------- + */ + + $nomNettoye = + preg_replace( + '/\s+derived\s*$/iu', + '', + $nomInvesting + ); + + + $nomNettoye = + trim( + $nomNettoye + ); + + + /* + * -------------------------------------------------------- + * RECHERCHE PAR NOM + * -------------------------------------------------------- + */ + + $cle = + normalizeName( + $nomNettoye + ); + + + $ref = + $referentiel[$cle] + ?? null; + + + /* + * -------------------------------------------------------- + * ALIAS + * + * Les noms Investing peuvent légèrement différer. + * -------------------------------------------------------- + */ + + if ($ref === null) { + + $aliases = [ + + '3m' => + '3M', + + 'amazon.com' => + 'Amazon.com', + + 'american express' => + 'American Express', + + 'amgen' => + 'Amgen', + + 'apple' => + 'Apple', + + 'boeing' => + 'Boeing', + + 'caterpillar' => + 'Caterpillar', + + 'chevron' => + 'Chevron', + + 'cisco' => + 'Cisco Systems', + + 'coca cola' => + 'Coca-Cola', + + 'disney' => + 'Disney', + + 'goldman sachs' => + 'Goldman Sachs', + + 'home depot' => + 'Home Depot', + + 'honeywell' => + 'Honeywell International', + + 'ibm' => + 'IBM', + + 'johnson johnson' => + 'Johnson & Johnson', + + 'jpmorgan' => + 'JPMorgan Chase', + + 'mcdonalds' => + 'McDonald\'s', + + 'merck' => + 'Merck & Co.', + + 'microsoft' => + 'Microsoft', + + 'nike' => + 'Nike', + + 'nvidia' => + 'NVIDIA', + + 'procter gamble' => + 'Procter & Gamble', + + 'salesforce' => + 'Salesforce', + + 'sherwin williams' => + 'Sherwin-Williams', + + 'travelers' => + 'Travelers', + + 'unitedhealth' => + 'UnitedHealth Group', + + 'verizon' => + 'Verizon Communications', + + 'visa' => + 'Visa', + + 'walmart' => + 'Walmart', + + 'alphabet' => + 'Alphabet' + + ]; + + + if ( + isset( + $aliases[$cle] + ) + ) { + + $cleAlias = + normalizeName( + $aliases[$cle] + ); + + + $ref = + $referentiel[$cleAlias] ?? null; + } + } + + + /* + * -------------------------------------------------------- + * ABSENT DU REFERENTIEL + * -------------------------------------------------------- + */ + + if ($ref === null) { + + $notFound[] = + $nomInvesting . + ' / ' . + $nomNettoye; + + + echo + "$logPrefix - ATTENTION : " . + "référentiel inconnu : " . + $nomInvesting . + " -> " . + $nomNettoye . + "\n"; + + + continue; + } + + + /* + * -------------------------------------------------------- + * REFERENTIEL + * -------------------------------------------------------- + */ + + $nom = + $ref['nom']; + + + $isin = + $ref['isin']; + + + $symbole = + $ref['symbole']; + + + /* + * -------------------------------------------------------- + * COURS + * -------------------------------------------------------- + */ + + $cours = + parseNumber( + $values[$columns['Last']] ?? null + ); + + + /* + * -------------------------------------------------------- + * PLUS HAUT + * -------------------------------------------------------- + */ + + $plusHaut = + parseNumber( + $values[$columns['High']] ?? null + ); + + + /* + * -------------------------------------------------------- + * PLUS BAS + * -------------------------------------------------------- + */ + + $plusBas = + parseNumber( + $values[$columns['Low']] ?? null + ); + + + /* + * -------------------------------------------------------- + * VARIATION % + * -------------------------------------------------------- + */ + + $variationPct = + parseNumber( + $values[$columns['Chg. %']] ?? null + ); + + + /* + * -------------------------------------------------------- + * VOLUME + * -------------------------------------------------------- + */ + + $volume = + parseVolume( + $values[$columns['Vol.']] ?? null + ); + + + /* + * -------------------------------------------------------- + * VARIATION ABSOLUE + * -------------------------------------------------------- + */ + + $variation = null; + + + if ( + + $cours !== null && + + $variationPct !== null && + + (100 + $variationPct) != 0 + + ) { + + $variation = + $cours * + $variationPct / + (100 + $variationPct); + } + + + /* + * -------------------------------------------------------- + * INSERTION + * -------------------------------------------------------- + */ + + try { + + $stmt->execute([ + + ':indice' => + 'DOWJONES', + + ':nom' => + $nom, + + ':isin' => + $isin, + + ':symbole' => + $symbole, + + ':cours' => + $cours, + + ':plus_haut' => + $plusHaut, + + ':plus_bas' => + $plusBas, + + ':variation' => + $variation, + + ':variation_pct' => + $variationPct, + + ':volume' => + $volume, + + ':date_cours' => + $dateCours, + + ':source' => + 'Investing' + + ]); + + + $count++; + + + echo + "$logPrefix - OK : " . + + str_pad( + (string)$count, + 2, + '0', + STR_PAD_LEFT + ) . + + " - " . + + $nom . + + " [" . + + $symbole . + + "] " . + + $isin . + + " - " . + + ($cours ?? 'NULL') . + + " - " . + + ($variationPct ?? 'NULL') . + + "%\n"; + } catch (PDOException $e) { + + $errors++; + + + echo + "$logPrefix - ERREUR SQL : " . + + $nom . + + " : " . + + $e->getMessage() . + + "\n"; + } +} + + +/* + * ============================================================ + * RESULTAT + * ============================================================ + */ + +echo "\n"; + + +echo +"$logPrefix - DOWJONES : " . + $count . + "/30 valeurs importées\n"; + + +/* + * ============================================================ + * VALEURS NON TROUVEES + * ============================================================ + */ + +if (count($notFound) > 0) { + + echo + "$logPrefix - VALEURS NON TROUVÉES DANS indice :\n"; + + + foreach ($notFound as $nom) { + + echo + " - " . + $nom . + "\n"; + } +} + + +/* + * ============================================================ + * ERREURS SQL + * ============================================================ + */ + +if ($errors > 0) { + + echo + "$logPrefix - Erreurs SQL : " . + $errors . + "\n"; +} + + +/* + * ============================================================ + * SECURITE + * ============================================================ + */ + +if ($count !== 30) { + + echo + "$logPrefix - ATTENTION : import incomplet !\n"; + + + exit(1); +} + + +echo +"$logPrefix - IMPORT DOWJONES OK\n"; + + +exit(0); + + +/* + * ============================================================ + * FONCTIONS + * ============================================================ + */ + + +/* + * ------------------------------------------------------------ + * NETTOYAGE TEXTE + * ------------------------------------------------------------ + */ + +function cleanText( + string $text +): string { + + $text = + html_entity_decode( + $text, + ENT_QUOTES | ENT_HTML5, + 'UTF-8' + ); + + + $text = + preg_replace( + '/\s+/u', + ' ', + $text + ); + + + return trim($text); +} + + +/* + * ------------------------------------------------------------ + * NORMALISATION NOM + * ------------------------------------------------------------ + */ + +function normalizeName( + string $name +): string { + + $name = + cleanText( + $name + ); + + + /* + * Apostrophes + */ + + $name = + str_replace( + [ + '’', + '`' + ], + "'", + $name + ); + + + /* + * Tirets + */ + + $name = + str_replace( + [ + '–', + '—' + ], + '-', + $name + ); + + + /* + * Espaces autour des tirets + */ + + $name = + preg_replace( + '/\s*-\s*/u', + '-', + $name + ); + + + /* + * Suppression de certains caractères + * de ponctuation pour faciliter + * les correspondances. + */ + + $name = + preg_replace( + '/[.,]/u', + '', + $name + ); + + + /* + * Espaces multiples + */ + + $name = + preg_replace( + '/\s+/u', + ' ', + $name + ); + + + return mb_strtolower( + trim($name), + 'UTF-8' + ); +} + + +/* + * ------------------------------------------------------------ + * PARSE NUMBER + * ------------------------------------------------------------ + */ + +function parseNumber( + ?string $value +): ?float { + + if ($value === null) { + return null; + } + + + $value = + cleanText( + $value + ); + + + if ($value === '') { + return null; + } + + + $value = + str_replace( + [ + "\xc2\xa0", + ' ', + '%' + ], + '', + $value + ); + + + /* + * Format français + */ + + if ( + + str_contains( + $value, + ',' + ) + + && + + !str_contains( + $value, + '.' + ) + + ) { + + $value = + str_replace( + ',', + '.', + $value + ); + } else { + + $value = + str_replace( + ',', + '', + $value + ); + } + + + $value = + preg_replace( + '/[^0-9.\-+]/', + '', + $value + ); + + + if ( + + $value === '' + + || + + !is_numeric( + $value + ) + + ) { + + return null; + } + + + return (float)$value; +} + + +/* + * ------------------------------------------------------------ + * PARSE VOLUME + * ------------------------------------------------------------ + */ + +function parseVolume( + ?string $value +): ?int { + + if ($value === null) { + return null; + } + + + $value = + cleanText( + $value + ); + + + if ($value === '') { + return null; + } + + + $value = + str_replace( + [ + "\xc2\xa0", + ' ' + ], + '', + $value + ); + + + $multiplier = 1; + + + $last = + strtoupper( + substr( + $value, + -1 + ) + ); + + + if ($last === 'K') { + + $multiplier = 1000; + + + $value = + substr( + $value, + 0, + -1 + ); + } elseif ($last === 'M') { + + $multiplier = 1000000; + + + $value = + substr( + $value, + 0, + -1 + ); + } elseif ($last === 'B') { + + $multiplier = 1000000000; + + + $value = + substr( + $value, + 0, + -1 + ); + } + + + $value = + str_replace( + ',', + '.', + $value + ); + + + $value = + preg_replace( + '/[^0-9.]/', + '', + $value + ); + + + if ( + + $value === '' + + || + + !is_numeric( + $value + ) + + ) { + + return null; + } + + + return (int)round( + (float)$value * + $multiplier + ); +}