Formati e fonti di dati 5 min di lettura

Parsing di XML: librerie, XPath ed esempi in vari linguaggi

Come lavorare con XML nei vari linguaggi: DOM contro SAX, XPath, namespace e lettura in streaming delle esportazioni di grandi dimensioni senza esaurire la memoria.

TW
Team Web-Scraping.it
Raccolta dati per le esigenze del business
Pubblicato il 21 aprile 2025

XML (eXtensible Markup Language) è un formato di testo per archiviare e trasmettere dati strutturati sotto forma di albero. Su di esso si basano i feed di prodotto di marketplace e aggregatori, i formati bancari e di interscambio, i file di configurazione e anche i suoi formati derivati: i feed RSS e le sitemap. Questo articolo fa parte della nostra panoramica generale sul parsing di documenti, con il focus proprio su XML.

Come è fatto un XML

Un documento XML è un albero di elementi. Ogni elemento ha un nome (il tag) e può avere un insieme di attributi, un contenuto testuale ed elementi annidati.

xml
<catalog>
  <product id="101" available="true">
    <name>Macinacaffè</name>
    <price currency="EUR">34.90</price>
  </product>
  <product id="102" available="false">
    <name>Bollitore</name>
    <price currency="EUR">21.90</price>
  </product>
</catalog>

La differenza chiave rispetto a JSON è che in XML i dati vivono in due posti contemporaneamente: nel testo degli elementi (<name>Macinacaffè</name>) e negli attributi (id="101"). Va tenuto a mente: in fase di estrazione, a volte interroghi il contenuto del tag, a volte i suoi attributi.

Un tema a parte sono i namespace (spazi dei nomi). Nei formati complessi i tag appaiono come <g:price> o <atom:link>, dove il prefisso è legato a un URI. I parser esigono che il namespace venga considerato nella ricerca dei nodi, altrimenti la selezione restituisce il vuoto — è la causa più frequente del «il parser non trova nulla, anche se i dati sono lì».

Due approcci: albero in memoria e lettura in streaming

Esistono due modi radicalmente diversi di fare il parsing. L’approccio DOM costruisce l’intero albero in memoria e offre una navigazione comoda, anche via XPath: è semplice e va bene per file fino a qualche decina di megabyte. L’approccio in streaming (SAX / iterparse) legge il file per eventi, senza caricarlo per intero, ed è insostituibile con esportazioni da centinaia di megabyte o da gigabyte. Per i compiti tipici basta quasi sempre il DOM; allo streaming si passa quando il file smette di stare in memoria.

Python

La libreria standard include il modulo xml.etree.ElementTree: è sufficiente per i casi semplici e non richiede installazione.

python
import xml.etree.ElementTree as ET

tree = ET.parse("catalog.xml")
root = tree.getroot()

for product in root.findall("product"):
    name = product.find("name").text
    price = product.find("price").text
    available = product.get("available")  # lettura dell'attributo
    print(name, price, available)

Per il lavoro serio si usa lxml: è più veloce, digerisce meglio l’XML «sporco» e supporta XPath al completo, namespace inclusi.

python
from lxml import etree

tree = etree.parse("catalog.xml")

# XPath: tutti i prodotti disponibili
for product in tree.xpath("//product[@available='true']"):
    name = product.xpath("string(name)")
    price = product.xpath("string(price)")
    print(name, price)

Se i dati sono arrivati da un’API in XML, ma è più comodo trattarli come un dizionario, aiuta la libreria xmltodict, che trasforma l’XML in normali strutture annidate di Python — molto vicino a come elaboreresti un JSON.

JavaScript / Node.js

In Node sono popolari due soluzioni. fast-xml-parser trasforma l’XML in un oggetto senza dipendenze esterne e lavora molto velocemente.

javascript
const { XMLParser } = require("fast-xml-parser");
const fs = require("fs");

const xml = fs.readFileSync("catalog.xml", "utf-8");
const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "@_" });
const data = parser.parse(xml);

for (const product of data.catalog.product) {
  console.log(product.name, product.price["#text"], product["@_available"]);
}

L’alternativa è xml2js, veterana dell’ecosistema e comoda per l’elaborazione asincrona. Nel browser è disponibile il DOMParser integrato, che permette di percorrere l’XML con gli stessi metodi di un documento HTML.

PHP

Per i documenti semplici è perfetto SimpleXML, integrato nel linguaggio: dà accesso agli elementi come se fossero proprietà di un oggetto.

php
<?php
$xml = simplexml_load_file("catalog.xml");

foreach ($xml->product as $product) {
    $name  = (string) $product->name;
    $price = (string) $product->price;
    $available = (string) $product["available"]; // attributo
    echo "$name — $price — $available\n";
}

Quando servono i namespace, XPath complessi o l’elaborazione in streaming di file grandi, si passa a DOMDocument e XMLReader, inclusi nella stessa dotazione standard di PHP.

Go

La libreria standard di Go include il pacchetto encoding/xml, che trasforma l’XML direttamente in struct tramite i tag dei campi.

go
package main

import (
    "encoding/xml"
    "fmt"
    "os"
)

type Catalog struct {
    Products []struct {
        ID        string `xml:"id,attr"`
        Available string `xml:"available,attr"`
        Name      string `xml:"name"`
        Price     string `xml:"price"`
    } `xml:"product"`
}

func main() {
    data, _ := os.ReadFile("catalog.xml")
    var c Catalog
    xml.Unmarshal(data, &c)
    for _, p := range c.Products {
        fmt.Println(p.Name, p.Price, p.Available)
    }
}

Errori tipici

Il più delle volte i problemi nascono dalla codifica: se la dichiarazione indica ISO-8859-1 (o windows-1252) e il file viene letto come UTF-8, i caratteri si trasformano in spazzatura — la codifica va lasciata al parser oppure convertita in modo esplicito. La seconda causa per frequenza sono i namespace ignorati, per colpa dei quali le query XPath restituiscono in silenzio un risultato vuoto. La terza è il tentativo di caricare per intero nel DOM un file troppo grande: un’esportazione da gigabyte va letta in streaming. E infine, non fare il parsing dell’XML con le espressioni regolari: il formato è annidato e ricorsivo, e le regex si rompono al primo caso fuori standard — per il testo senza struttura sono giustificate (vedi il parsing di TXT), ma non per l’XML.

Se dalla tua parte c’è uno schema di esportazione non standard, namespace rotti o file da gigabyte — configuriamo il parsing di XML su misura per il tuo formato. E se i dati di origine non arrivano in XML puro, ma nei suoi derivati, guarda i materiali affini: RSS e sitemap.