Nel mondo dello sviluppo web, spesso ci troviamo a dover estrarre dati da
pagine HTML
per elaborarli o visualizzarli in altri formati. Una delle
operazioni comuni è convertire elenchi HTML in file CSV, al fine di memorizzare
le informazioni all'interno di una base dati e successivamente eseguire
operazioni di reportistica. In questo articolo, eseguiremo tale operazione
usando il linguaggio di programmazione Javascript.
La conversione da HTML a CSV, infatti, è utile in molte situazioni, come l'automazione di report, l'analisi dei dati estratti da pagine web, e molto altro. JavaScript si rivela uno strumento potente per manipolare il DOM (Document Object Model) e estrarre dati da pagine web, rendendolo ideale per questo compito.
In questo articolo, esploreremo come eseguire questa operazione in modo semplice e efficace. Il codice mostrato nell'articolo può essere eseguito su qualsiasi piattaforma poiché lavora con Docker.
Prima di iniziare, assicurati di avere le conoscenze di base di HTML, CSS e JavaScript. Inoltre, è utile avere familiarità con i concetti di selezione degli elementi del DOM e manipolazione degli array in JavaScript.
Obiettivo
L'articolo ti mostra come prelevare le informazioni dal seguente frammento di codice HTML:
<ul class="dropdown-menu inner show" role="presentation" style="margin-top: 0px; margin-bottom: 0px;">
<li>
<a role="option" class="dropdown-item" id="bs-select-1-0" tabindex="0">
<span class="text">AL: Albania</span>
</a>
</li>
<li>
<a role="option" class="dropdown-item" id="bs-select-1-1" tabindex="0">
<span class="text">AD: Andorra</span>
</a>
</li>
<li>
<a role="option" class="dropdown-item" id="bs-select-1-2" tabindex="0">
<span class="text">AM: Armenia</span>
</a>
</li>
<!-- Altri li con le nazioni -->
</ul>
e memorizzarle all'interno di un file csv, come segue:
ABBREVIATION,NAME
AL,Albania
AD,Andorra
AM,Armenia
...
Librerie utilizzate
Nello script che andremo a vedere, sono utilizzate le librerie fs
, cheerio
e csv-writer
.
fs
(File System) è un modulo in Node.js che fornisce un'API per interagire con il file system del sistema operativo.- cheerio è una libreria di parsing e manipolazione di HTML, ispirata a jQuery, ma ottimizzata per l'utilizzo in ambienti server-side con Node.js.
csv-writer
è una libreria Node.js che semplifica la scrittura di dati in formato CSV (Comma-Separated Values).
Estrazione dati
Il primo passo da eseguire è quello di prelevare la regola CSS per prelevare il
testo contenuto all'interno dell'elemento <span>
. La regola che possiamo
individuare è: li a .text
.
Quindi per poter eseguire dei cicli su tutti gli elementi dell'elenco puntato, possiamo eseguire il seguente codice:
$('li a .text').each((index, element) => {
const text = $(element).text();
console.log(text);
});
Il precedente codice stampa su console le nazioni presenti sul nostro file.
Tuttavia ogni riga ha due componenti, la sigla e il nome, come segue IT: Italia
. La fortuna è che questa struttura si ripete per ogni riga. Possiamo
quindi prelevare le informazioni, memorizzate nella costante text
, come segue:
const [abbreviation, name] = text.split(': ').map(item => item.trim());
adesso abbreviation
memorizza la sigla e name
il nome della nazione.
A questo punto possiamo eseguire la memorizzazione delle nazioni all'interno di un array, come segue:
// Definizione dell'array
const nations = [];
$('li a .text').each((index, element) => {
const text = $(element).text();
const [abbreviation, name] = text.split(': ').map(item => item.trim());
// Memorizzazione nell'array come oggetto
nations.push({ abbreviation, name });
});
Il precedente codice estrae le nazioni dall'HTML e le memorizza nel javascript. Stampando l'array otterremo:
[
{ abbreviation: 'AL', name: 'Albania' },
{ abbreviation: 'AD', name: 'Andorra' },
{ abbreviation: 'AM', name: 'Armenia' },
]
Scrittura su file CSV
A questo punto del codice, all'interno di nations
abbiamo tutte le nazioni
memorizzate nella struttura dati mostrata. Ora è il momento di creare il file csv
.
Per fare ciò, inizializziamo un oggetto csvWriter
, come segue:
const OUTPUT_FILEPATH = '/app/output/nazioni.csv';
const csvWriter = createCsvWriter({
path: OUTPUT_FILEPATH,
header: [
{ id: 'abbreviation', title: 'ABBREVIATION' },
{ id: 'name', title: 'NAME' }
]
});
Nel frammento di codice si nota che il path sul quale sarà scritto il file è
indicato nella costante OUTPUT_FILEPATH
. Inoltre, il file csv prodotto avrà
una prima riga di header, all'interno della quale saranno scritti i nomi delle
colonne.
Con l'oggetto csvWriter
creato, possiamo scrivere gli elementi dell'array, come segue:
csvWriter.writeRecords(nations)
.then(() => {
console.log('File CSV creato con successo');
});
Al termine della scrittura del file csv, il programma notifica l'avvenuta scrittura del file.
Script completo
Possiamo riportare lo script completo per l'estrazione dei dati, che deve
essere memorizzato nel file src/extract.js
:
const INPUT_FILEPATH = '/app/input/nazioni.html';
const OUTPUT_FILEPATH = '/app/output/nazioni.csv';
const fs = require('fs');
const cheerio = require('cheerio');
const createCsvWriter = require('csv-writer').createObjectCsvWriter;
// Leggi il file HTML
const html = fs.readFileSync(INPUT_FILEPATH, 'utf8');
const $ = cheerio.load(html);
const nations = [];
// Estrai le informazioni
$('li a .text').each((index, element) => {
const text = $(element).text();
const [abbreviation, name] = text.split(': ').map(item => item.trim());
nations.push({ abbreviation, name });
});
// Configura il writer CSV
const csvWriter = createCsvWriter({
path: OUTPUT_FILEPATH,
header: [
{ id: 'abbreviation', title: 'ABBREVIATION' },
{ id: 'name', title: 'NAME' }
]
});
// Scrivi i dati nel file CSV
csvWriter.writeRecords(nations)
.then(() => {
console.log('File CSV creato con successo');
});
Esecuzione dello script usando Docker
Per eseguire lo script implementato, configuriamo un ambiente isolato usando Docker.
La configurazione di un ambiente Docker per eseguire codice usando Node.js prevede:
- Creazione del
Dockerfile
- Creazione del
package.json
- Creazione del
docker-compose.yml
- Creazione cartelle locali
Build
eUp
del container
1. Creazione Dockerfile
Crea un file chiamato Dockerfile
, all'interno della cartella principale del
tuo progetto, aggiungendo il seguente contenuto:
# Usa un'immagine base con Node.js
FROM node:20
# Imposta la directory di lavoro all'interno del container
WORKDIR /app
# Copia il file package.json e package-lock.json (se presente)
COPY package*.json ./
# Installa le dipendenze del progetto
RUN npm install
# Copia il contenuto della cartella contenente il codice sorgente sul container
COPY src /app/src
2. Creazione package.json
Crea un file chiamato package.json
, all'interno della cartella principale del
tuo progetto, con il seguente contenuto:
{
"name": "html-extractor",
"version": "1.0.0",
"description": "Script per estrarre dati HTML e creare un file CSV",
"scripts": {
"start": "node src/extract.js"
},
"dependencies": {
"cheerio": "^1.0.0-rc.10",
"csv-writer": "^1.6.0"
}
}
3. Creazione docker-compose.yml
Crea un file chiamato docker-compose.yml
, all'interno della cartella
principale del tuo progetto, con il seguente contenuto:
services:
nodeapp:
build: .
volumes:
- ./input:/app/input
- ./output:/app/output
command: npm start
come si evince il docker-compose.yml
imposta due volumi sulle cartelle locali
input
e output
.
4. Creazione cartelle locali
Prima di avviare il container, occorre creare le cartelle che saranno montate sui volumi Docker. Quindi puoi creare all'interno della cartella principale le cartelle input
e output
.
La struttura delle cartelle prima dell'esecuzione del container dovrà essere come segue:
cartella_progetto
|- Dockerfile
|- docker-compose.yml
|- package.json
|- input
|- output
|- src
|- extract.js
All'interno della cartella di input devi inserire il file delle nazioni che lo script leggerà. Di default nell'esempio il file l'ho chiamato nazioni.html
ed il contenuto è il seguente:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nazioni Europee</title>
</head>
<body>
<h1>Nazioni Europee</h1>
<ul class="dropdown-menu inner show" role="presentation" style="margin-top: 0px; margin-bottom: 0px;">
<li><a role="option" class="dropdown-item" id="bs-select-1-0" tabindex="0"><span class="text">AL: Albania</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-1" tabindex="0"><span class="text">AD: Andorra</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-2" tabindex="0"><span class="text">AM: Armenia</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-3" tabindex="0"><span class="text">AT: Austria</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-4" tabindex="0"><span class="text">AZ: Azerbaijan</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-5" tabindex="0"><span class="text">BY: Belarus</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-6" tabindex="0"><span class="text">BE: Belgium</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-7" tabindex="0"><span class="text">BA: Bosnia and Herzegovina</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-8" tabindex="0"><span class="text">BG: Bulgaria</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-9" tabindex="0"><span class="text">HR: Croatia</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-10" tabindex="0"><span class="text">CY: Cyprus</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-11" tabindex="0"><span class="text">CZ: Czech Republic</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-12" tabindex="0"><span class="text">DK: Denmark</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-13" tabindex="0"><span class="text">EE: Estonia</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-14" tabindex="0"><span class="text">FI: Finland</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-15" tabindex="0"><span class="text">FR: France</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-16" tabindex="0"><span class="text">GE: Georgia</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-17" tabindex="0"><span class="text">DE: Germany</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-18" tabindex="0"><span class="text">GR: Greece</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-19" tabindex="0"><span class="text">HU: Hungary</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-20" tabindex="0"><span class="text">IS: Iceland</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-21" tabindex="0"><span class="text">IE: Ireland</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-22" tabindex="0"><span class="text">IT: Italy</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-23" tabindex="0"><span class="text">KZ: Kazakhstan</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-24" tabindex="0"><span class="text">LV: Latvia</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-25" tabindex="0"><span class="text">LI: Liechtenstein</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-26" tabindex="0"><span class="text">LT: Lithuania</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-27" tabindex="0"><span class="text">LU: Luxembourg</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-28" tabindex="0"><span class="text">MT: Malta</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-29" tabindex="0"><span class="text">MD: Moldova</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-30" tabindex="0"><span class="text">MC: Monaco</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-31" tabindex="0"><span class="text">ME: Montenegro</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-32" tabindex="0"><span class="text">NL: Netherlands</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-33" tabindex="0"><span class="text">MK: North Macedonia</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-34" tabindex="0"><span class="text">NO: Norway</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-35" tabindex="0"><span class="text">PL: Poland</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-36" tabindex="0"><span class="text">PT: Portugal</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-37" tabindex="0"><span class="text">RO: Romania</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-38" tabindex="0"><span class="text">RU: Russia</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-39" tabindex="0"><span class="text">SM: San Marino</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-40" tabindex="0"><span class="text">RS: Serbia</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-41" tabindex="0"><span class="text">SK: Slovakia</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-42" tabindex="0"><span class="text">SI: Slovenia</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-43" tabindex="0"><span class="text">ES: Spain</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-44" tabindex="0"><span class="text">SE: Sweden</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-45" tabindex="0"><span class="text">CH: Switzerland</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-46" tabindex="0"><span class="text">TR: Turkey</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-47" tabindex="0"><span class="text">UA: Ukraine</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-48" tabindex="0"><span class="text">GB: United Kingdom</span></a></li>
<li><a role="option" class="dropdown-item" id="bs-select-1-49" tabindex="0"><span class="text">VA: Vatican City</span></a></li>
</ul>
</body>
</html>
Build
e Up
del container
L'esecuzione dello script per l'estrazione del file csv
prevede l'esecuzione del comando:
docker-compose up --build
se l'esecuzione va a buon fine, nella cartella output
troverai il file nazioni.csv
.
Conclusioni
Nell'articolo hai visto come poter estrarre informazioni dalle pagine HTML memorizzando le stesse su file CSV. Queste operazioni sono molto utili per poter eseguire attività di reportistica o di semplice scambio di dati tra applicazioni diverse.
L'articolo ha basato l'implementazione del codice sul linguaggio di programmazione Javascript su ambiente Docker.