sty 022012

Jak parsować duże pliki XML w PHP?

Jakiś czas temu stanąłem przed problemem parsowania dużych plików XML w PHP. O ile z małymi plikami nie ma problemu i całość parsowana jest szybko, to próba obsługi większych plików często powoduje zatrzymywanie wykonywania skryptów. Tak duże pliki są jednak często wykorzystywane przy zdalnych aktualizacjach ofert (np. publikowanych przez hurtownie).

Dzieje się tak, gdyż PHP ma z góry założone ograniczenie na możliwą do wykorzystania pamięć, a parsowanie plików standardową metodą (np. DOMDocument) potrafi ją skutecznie wykorzystać.

Rozwiązaniem jest skorzystanie z klasy XMLReader, domyślnie dostępnej w standardowej konfiguracji PHP od wersji 5.1.0 .

Zrobiłem krótkie porównanie szybkości działań dla DOMDocument i XMLReader korzystając z 4 różnych komputerów

Rozmiar pliku XML: 208 MB
Liczba wpisów: 148723

 DOMDocumentXMLReader
Lokalny komnputer269 sek41 sek.
Serwer dedykowany / Hetzner264 sek.15 sek.
Serwer współdzielony / vipserv.orgerror 500 / timeout15 sek.
Serwer współdzielony / IQ.pl277 sek.33 sek.

Jak widać różnica jest kolosalna (ok. 10-20 razy szybciej) i w przypadku dużych plików warto postawić na XMLReader.

Fragment kodu odpowiedzialny za parsowanie przez DOMDocument:

  1. $doc = new DOMDocument();
  2. $doc->load($localurl);
  3. $items= $doc->getElementsByTagName("item");
  4. $countItems = $items->length;
  5.  
  6. foreach($items as $item)
  7. {
  8. $id = $item->getElementsByTagName("id")->item(0)->nodeValue;
  9. $url = $item->getElementsByTagName("url")->item(0)->nodeValue;
  10. $title = $item->getElementsByTagName("title")->item(0)->nodeValue;
  11. $author = $item->getElementsByTagName("author")->item(0)->nodeValue;
  12. $isbn = $item->getElementsByTagName("isbn")->item(0)->nodeValue;
  13. $image = $item->getElementsByTagName("image")->item(0)->nodeValue;
  14. $ean = $item->getElementsByTagName("ean")->item(0)->nodeValue;
  15. $published = $item->getElementsByTagName("published")->item(0)->nodeValue;
  16. $publisher = $item->getElementsByTagName("publisher")->item(0)->nodeValue;
  17. $pages = $item->getElementsByTagName("pages")->item(0)->nodeValue;
  18. $price = $item->getElementsByTagName("price")->item(0)->nodeValue;
  19. $description = $item->getElementsByTagName("description")->item(0)->nodeValue;
  20. $status = $item->getElementsByTagName("status")->item(0)->nodeValue;
  21. $count++;
  22. }

 

Fragment kodu odpowiedzialny za parsowanie przez XMLReader:

  1. $reader = new XMLReader();
  2. $reader->open($localurl);
  3.  
  4. while($reader->read())
  5. {
  6. if($reader->nodeType == XMLReader::ELEMENT) $nodeName = $reader->name;
  7. if($reader->nodeType == XMLReader::TEXT || $reader->nodeType == XMLReader::CDATA)
  8. {
  9. if ($nodeName == 'id') $id = $reader->value;
  10. if ($nodeName == 'url') $url = $reader->value;
  11. if ($nodeName == 'title') $title = $reader->value;
  12. if ($nodeName == 'author') $author = $reader->value;
  13. if ($nodeName == 'isbn') $isbn = $reader->value;
  14. if ($nodeName == 'image') $image = $reader->value;
  15. if ($nodeName == 'ean') $ean = $reader->value;
  16. if ($nodeName == 'published') $published = $reader->value;
  17. if ($nodeName == 'publisher') $publisher = $reader->value;
  18. if ($nodeName == 'pages') $pages = $reader->value;
  19. if ($nodeName == 'price') $price = $reader->value;
  20. if ($nodeName == 'description') $description = $reader->value;
  21. if ($nodeName == 'status') $status = $reader->value;
  22. $ean = '';
  23. }
  24.  
  25. if($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == 'item')
  26. {
  27. $count++;
  28. }
  29. }
  30. $reader->close();

 

Kategoria: Programowanie Tagged with , , 3 komentarzy
3 Responses to Jak parsować duże pliki XML w PHP?
  1. Anonymous

    W pętli porównującej $nodeName warto dodać wykluczenia else i przerwania continue – będzie jeszcze szybciej ;)

  2. grzegorz

    Fakt, dobra sugestia.

  3. krzysztof

    Muszę to przetestować. Dotychczas gdy zdażało mi się przetwarzać duże pliki XML – to niestety wykonanie skryptu padało. Nawet gdy importowałem XML przy pomocy phpMyAdmin do bazy MySQL’a.

Dodaj komentarz

Your email address will not be published. Please enter your name, email and a comment.