Vous avez trouvé des données sur un site web. Vous en avez besoin dans votre base de données. Le workflow habituel : exporter en CSV, importer dans un outil SQL, gérer les incompatibilités de types, corriger les problèmes d'encodage.
Et si vous pouviez passer directement du tableau HTML à du SQL valide ?
Ce guide montre comment générer des instructions CREATE TABLE et INSERT INTO à partir de n'importe quel tableau web, en gérant l'inférence de types, l'assainissement des identifiants et l'échappement correct. C'est l'approche que j'utilise dans HTML Table Exporter.
Le Résultat Final
À partir d'un tableau comme celui-ci :
| Nom du Produit | Prix | En Stock |
|-----------------|--------|----------|
| Widget Pro | 29.99 | Oui |
| Gadget Basic | 14.50 | Non |
Générer :
-- Exporté depuis HTML Table Exporter PRO
CREATE TABLE produits (
nom_du_produit TEXT,
prix REAL,
en_stock TEXT
);
INSERT INTO produits (nom_du_produit, prix, en_stock) VALUES
('Widget Pro', 29.99, 'Oui'),
('Gadget Basic', 14.50, 'Non');
Construisons cela étape par étape.
Étape 1 : Extraire la Matrice du Tableau
D'abord, extraire les données brutes du tableau HTML :
function extractTableMatrix(table) {
const rows = Array.from(table.rows);
const grid = [];
rows.forEach((rowEl, rowIndex) => {
if (!grid[rowIndex]) grid[rowIndex] = [];
let colIndex = 0;
Array.from(rowEl.cells).forEach(cell => {
while (grid[rowIndex][colIndex] !== undefined) colIndex++;
const text = cell.textContent.trim();
const rowSpan = parseInt(cell.rowSpan) || 1;
const colSpan = parseInt(cell.colSpan) || 1;
for (let r = 0; r < rowSpan; r++) {
if (!grid[rowIndex + r]) grid[rowIndex + r] = [];
for (let c = 0; c < colSpan; c++) {
grid[rowIndex + r][colIndex + c] = text;
}
}
colIndex += colSpan;
});
});
return grid;
}
Sortie : Un tableau 2D où rows[0] est les en-têtes et rows[1+] sont les données.
Étape 2 : Assainir les Noms de Colonnes
Les identifiants SQL ont des règles strictes. Des en-têtes comme « Nom du Produit » ou « Prix (€) » nécessitent un assainissement :
function sanitizeSqlIdentifier(header, fallbackIndex) {
let id = (header || "").toString().trim();
if (!id) {
return `col_${fallbackIndex + 1}`;
}
// Normaliser l'unicode (supprimer les accents)
id = id.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
// Convertir en snake_case minuscules
id = id
.toLowerCase()
.replace(/[^a-z0-9]+/g, "_")
.replace(/^_+|_+$/g, "");
// S'assurer que ça ne commence pas par un chiffre
if (/^\d/.test(id)) {
id = `col_${id}`;
}
if (!id) {
return `col_${fallbackIndex + 1}`;
}
return id;
}
// Exemples :
// "Nom du Produit" → "nom_du_produit"
// "Prix (€)" → "prix"
// "2024 Revenus" → "col_2024_revenus"
// "" → "col_1"
Étape 3 : Gérer les Noms de Colonnes en Double
Les tableaux ont parfois des en-têtes dupliqués. SQL exige des noms de colonnes uniques :
function makeUniqueColumnNames(headers) {
const used = new Set();
return headers.map((header, idx) => {
let name = sanitizeSqlIdentifier(header, idx);
let candidate = name;
let counter = 1;
while (used.has(candidate)) {
candidate = `${name}_${counter}`;
counter++;
}
used.add(candidate);
return candidate;
});
}
// ["Nom", "Nom", "Valeur"] → ["nom", "nom_1", "valeur"]
Étape 4 : Inférer les Types SQL
Analyser les données pour déterminer les types SQL appropriés :
function inferSqlColumnTypes(rows, headerRowIndex = 0) {
const headerRow = rows[headerRowIndex] || [];
const dataRows = rows.slice(headerRowIndex + 1);
const types = new Array(headerRow.length).fill("TEXT");
for (let col = 0; col < headerRow.length; col++) {
const values = [];
for (let r = 0; r < Math.min(dataRows.length, 50); r++) {
const cell = dataRows[r][col];
const v = cell != null ? String(cell).trim() : "";
if (v !== "") values.push(v);
}
if (values.length === 0) {
types[col] = "TEXT";
continue;
}
let allInt = true;
let allNumeric = true;
for (const v of values) {
if (!/^[-+]?\d+$/.test(v)) {
allInt = false;
}
if (!/^[-+]?\d+([.,]\d+)?$/.test(v)) {
allNumeric = false;
}
}
if (allInt) {
types[col] = "INTEGER";
} else if (allNumeric) {
types[col] = "REAL";
} else {
types[col] = "TEXT";
}
}
return types;
}
Priorité des types : INTEGER > REAL > TEXT
Étape 5 : Échapper les Valeurs Correctement
L'injection SQL n'est pas seulement un problème de sécurité — elle corrompt vos données. Échappement correct :
function sqlEscapeValue(raw, type) {
if (raw == null) return "NULL";
const v = String(raw).trim();
if (v === "") return "NULL";
if (type === "INTEGER" || type === "REAL") {
const normalized = v.replace(",", ".");
const num = Number(normalized);
if (!Number.isNaN(num) && Number.isFinite(num)) {
return normalized;
}
}
// TEXT : échapper les apostrophes en les doublant
const escaped = v.replace(/'/g, "''");
return `'${escaped}'`;
}
// Exemples :
// sqlEscapeValue("Bonjour", "TEXT") → "'Bonjour'"
// sqlEscapeValue("C'est bon", "TEXT") → "'C''est bon'"
// sqlEscapeValue("29.99", "REAL") → "29.99"
// sqlEscapeValue("", "TEXT") → "NULL"
// sqlEscapeValue(null, "TEXT") → "NULL"
Étape 6 : Générer le SQL
En assemblant le tout :
function tableToSql(tableInfo) {
const rows = tableInfo.rows || [];
if (rows.length < 2) return "";
const headerRowIndex = tableInfo.headerRowIndex || 0;
const headerRow = rows[headerRowIndex];
const dataRows = rows.slice(headerRowIndex + 1);
if (!headerRow || headerRow.length === 0) return "";
const columnNames = makeUniqueColumnNames(headerRow);
const types = inferSqlColumnTypes(rows, headerRowIndex);
const rawTableName = tableInfo.name || tableInfo.slug || "table";
const tableName = sanitizeSqlIdentifier(rawTableName, 0) || "table_export";
// Instruction CREATE TABLE
const createLines = columnNames.map((col, i) =>
` ${col} ${types[i] || "TEXT"}`
);
const createStmt = `CREATE TABLE ${tableName} (\n${createLines.join(",\n")}\n);`;
// Instructions INSERT
const insertHeader = `INSERT INTO ${tableName} (${columnNames.join(", ")}) VALUES`;
const valueLines = dataRows.map(row => {
const values = columnNames.map((_, i) => {
const cell = row[i];
const type = types[i] || "TEXT";
return sqlEscapeValue(cell, type);
});
return ` (${values.join(", ")})`;
});
let sql = `-- Exporté depuis HTML Table Exporter\n\n${createStmt}\n\n`;
if (valueLines.length > 0) {
sql += `${insertHeader}\n${valueLines.join(",\n")};\n`;
}
return sql;
}
Gestion des Cas Limites
Valeurs NULL
De nombreux tableaux web représentent les données manquantes différemment :
const NULL_PATTERNS = ["N/A", "n/a", "-", "--", "null", "none", "."];
function sqlEscapeValue(raw, type, nullPatterns = NULL_PATTERNS) {
if (raw == null) return "NULL";
const v = String(raw).trim();
if (v === "" || nullPatterns.includes(v.toLowerCase())) {
return "NULL";
}
// ... reste de la fonction
}
Grands Jeux de Données
Pour les tableaux avec des milliers de lignes, regroupez les INSERT par lots :
function tableToSqlBatched(tableInfo, batchSize = 1000) {
// ... même configuration ...
const batches = [];
for (let i = 0; i < valueLines.length; i += batchSize) {
const batch = valueLines.slice(i, i + batchSize);
batches.push(`${insertHeader}\n${batch.join(",\n")};`);
}
return `${createStmt}\n\n${batches.join("\n\n")}`;
}
Syntaxe Spécifique aux Bases de Données
SQLite, PostgreSQL et MySQL ont de légères différences de syntaxe :
function tableToSql(tableInfo, dialect = "sqlite") {
// ... même logique colonnes/types ...
const typeMap = {
sqlite: { INTEGER: "INTEGER", REAL: "REAL", TEXT: "TEXT" },
postgresql: { INTEGER: "INTEGER", REAL: "NUMERIC", TEXT: "TEXT" },
mysql: { INTEGER: "INT", REAL: "DECIMAL(10,2)", TEXT: "VARCHAR(255)" }
};
const dialectTypes = typeMap[dialect] || typeMap.sqlite;
const createLines = columnNames.map((col, i) =>
` ${col} ${dialectTypes[types[i]] || dialectTypes.TEXT}`
);
// ... reste de la fonction ...
}
Quand Utiliser Ceci
Adapté pour :
- Imports rapides de données dans SQLite/PostgreSQL
- Peupler des bases de données de test
- Migrations de données ponctuelles
- Apprendre le SQL par l'exemple
Moins adapté pour :
- ETL à grande échelle (utilisez des outils dédiés)
- Schémas complexes (clés étrangères, contraintes)
- Données binaires
Pour des pipelines de données en production, des outils ETL avec validation sont préférables. Pour « j'ai besoin de ce tableau dans ma base de données maintenant », la génération directe de SQL est rapide.
Essayez-le Sans Code
Si vous ne voulez pas construire tout cela vous-même, HTML Table Exporter PRO génère du SQL en un clic. La version gratuite exporte en CSV/JSON/Excel ; la version PRO ajoute SQL, NDJSON et le nettoyage de données.
Pour une comparaison des meilleures extensions d'export, consultez notre guide des scrapers de tableaux HTML pour Chrome.
En savoir plus sur gauchogrid.com/fr/html-table-exporter ou essayez-le sur le Chrome Web Store.
Dans quelle base de données importez-vous des données web ? Je suis curieux de connaître les cas d'utilisation.
Top comments (0)