67 votes

Analyse d'énormes fichiers XML en PHP

J'essaie d'analyser les fichiers XML du contenu et des structures de DMOZ dans MySQL, mais tous les scripts existants pour ce faire sont très anciens et ne fonctionnent pas bien. Comment puis-je ouvrir un gros fichier XML (1 Go) en PHP pour l'analyser ?

0 votes

amolnpujari.wordpress.com/2012/03/31/reading_huge_xml-rb il est si simple de traiter de gros fichiers xml en ruby

5voto

Frank Farmer Points 16159

Ce n'est pas une solution idéale, mais c'est juste pour proposer une autre option :

Vous pouvez diviser de nombreux fichiers XML volumineux en morceaux, notamment ceux qui ne sont que des listes d'éléments similaires (comme le fichier sur lequel vous travaillez).

Par exemple, si votre doc ressemble à.. :

<dmoz>
  <listing>....</listing>
  <listing>....</listing>
  <listing>....</listing>
  <listing>....</listing>
  <listing>....</listing>
  <listing>....</listing>
  ...
</dmoz>

Vous pouvez le lire en une méga ou deux à la fois, envelopper artificiellement le peu de complet <listing> que vous avez chargées dans une balise de niveau Root, puis les charger via simplexml/domxml (j'ai utilisé domxml, en adoptant cette approche).

Franchement, je préfère cette approche si vous utilisez PHP < 5.1.2. Avec 5.1.2 et plus, XMLReader est disponible, ce qui est probablement la meilleure option, mais avant cela, vous êtes coincé avec soit la stratégie de chunking ci-dessus, soit la vieille librairie SAX/expat. Et je ne sais pas pour le reste d'entre vous, mais je déteste écrire/maintenir des parsers SAX/expat.

Notez, cependant, que cette approche n'est PAS vraiment pratique lorsque votre document n'a pas se composer de nombreux éléments identiques de niveau inférieur (par exemple, cela fonctionne très bien pour toute sorte de liste de fichiers, ou d'URL, etc., mais cela n'aurait aucun sens pour l'analyse d'un gros document HTML)

2voto

Thomas Weinert Points 31

Vous pouvez combiner XMLReader avec DOM pour cela. En PHP, les deux API (et SimpleXML) sont basées sur la même bibliothèque - libxml2. Les grands XML sont généralement une liste d'enregistrements. Vous utilisez donc XMLReader pour itérer les enregistrements, charger un seul enregistrement dans DOM et utiliser les méthodes DOM et Xpath pour extraire les valeurs. La clé est la méthode XMLReader::expand() . Il charge le nœud actuel dans une instance XMLReader et ses descendants en tant que nœuds DOM.

Exemple XML :

<books>
  <book>
    <title isbn="978-0596100087">XSLT 1.0 Pocket Reference</title>
  </book>
  <book>
    <title isbn="978-0596100506">XML Pocket Reference</title>
  </book>
  <!-- ... -->
</books>

Exemple de code :

// open the XML file
$reader = new XMLReader();
$reader->open('books.xml');

// prepare a DOM document
$document = new DOMDocument();
$xpath = new DOMXpath($document);

// find the first `book` element node at any depth
while ($reader->read() && $reader->localName !== 'book') {
  continue;
}

// as long as here is a node with the name "book"
while ($reader->localName === 'book') {
  // expand the node into the prepared DOM
  $book = $reader->expand($document);
  // use Xpath expressions to fetch values
  var_dump(
    $xpath->evaluate('string(title/@isbn)', $book),
    $xpath->evaluate('string(title)', $book)
  );
  // move to the next book sibling node
  $reader->next('book');
}
$reader->close();

Notez que le nœud développé n'est jamais ajouté au document DOM. Cela permet au GC de le nettoyer.

Cette approche fonctionne également avec les espaces de noms XML.

$namespaceURI = 'urn:example-books';

$reader = new XMLReader();
$reader->open('books.xml');

$document = new DOMDocument();
$xpath = new DOMXpath($document);
// register a prefix for the Xpath expressions
$xpath->registerNamespace('b', $namespaceURI);

// compare local node name and namespace URI
while (
  $reader->read() &&
  (
    $reader->localName !== 'book' ||
    $reader->namespaceURI !== $namespaceURI
  )
) {
  continue;
}

// iterate the book elements 
while ($reader->localName === 'book') {
  // validate that they are in the namespace
  if ($reader->namespaceURI === $namespaceURI) {
    $book = $reader->expand($document);
    var_dump(
      $xpath->evaluate('string(b:title/@isbn)', $book),
      $xpath->evaluate('string(b:title)', $book)
    );
  }
  $reader->next('book');
}
$reader->close();

0voto

Alex Points 43

J'ai testé le code suivant avec un xml de 2 Go :

<?php
set_time_limit(0);
$reader = new XMLReader();
if (!$reader->open("data.xml"))
{
    die("Failed to open 'data.xml'");
}
while($reader->read())
{
    $node = $reader->expand();
    // process $node...
}
$reader->close();
?>

0voto

Emil Dworniczak Points 15

Ma solution :

$reader = new XMLReader();
$reader->open($fileTMP);
 while ($reader->read()) {
 if ($reader->nodeType === XMLReader::ELEMENT && $reader->name === 'xmltag' && $reader->isEmptyElement === false) {
 $item = simplexml_load_string($reader->readOuterXML(), null, LIBXML_NOCDATA); 
   //operations on file
}
}
$reader->close();

0voto

Nikolay Gechev Points 9

La manière très performante est

preg_split('/(<|>)/m', $xmlString);

Et après cela, un seul cycle est nécessaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X