Как сделать xml parser.

Вот и лето пришло и первая неделя июля пролетела незаметно. Через две недели мне защищать свой диплом, а одна из частей его - XML parser. Но жалко, что не в пхп. Ну ниче, счас наверстаем :)

Я видел много xml parser`ов, но не затрагивал при этом веб-программирование. Теперь же я хочу выяснить и научиться вместе с вами, как сделать простой xml parser в php.

А зачем? Надо! :)

Не, ну на самом деле: xml-файлы - очень полезная штука. И любой профессионал должен… нет, не должен, а обязан знать, как с ними работать. Мы же хотим стать профессионалами? Если Вы на моем блоге, то такое желание у Вас есть.

Итак…

Мы предполагаем, что знаем, что такое XML и описывать его здесь не будем. Ну, если не знаем, то легко узнаем здесь: https://ru.wikipedia.org/wiki/XML

При поиске способов парсинга XML на PHP, я обнаружил простой набор функций в PHP для работы с XML-файлами, который называется «XML Parser Functions». Парсинг начинается с инициализации парсера вызовом функции xml_parser_create:

$xml_parser = xml_parser_create();

Потом нам нужно сказать парсеру, какие функции будут обрабатывать попадающиеся ему xml-теги и текстовую информацию в процессе парсинга. Т.е. нужно установить некие обработчики:

xml_set_element_handler($xml_parser, “startElement”, “endElement”);

Эта функция отвечает за установку обработчиков начала элемента и конца элемента. Например, если в тексте xml-файла встретится комбинация , то функция startElement сработает, когда парсер найдет элемент , а функция endElement - при нахождении .

Сами же функции startElement и endElement принимают несколько параметров согласно документации по php:

<?
function startElement($parser, $name, $attrs) {
    // $parser - уникальный идентификатор парсера
//     (т.к. мы можем использовать несколько парсеров)
// $name - имя обнаруженного элемента
// $attrs - массив атрибутов обнаруженного элемента
}

function endElement($parser, $name) {
    // $parser - уникальный идентификатор парсера
// $name - имя обнаруженного элемента
}
?>

А как же считывать данные из файла? Мы же пока не видели ни одного параметра для этого ни в одной из функций! А об этом дальше: считывание файла возлагается на плечи программиста, т.е. мы должны использовать стандартные функции для работы с файлами:

<?
if (!($fp = fopen($file, "r"))) {
    die("could not open XML input");
}
?>

Открыли файл. А теперь нужно построчно считывать его и скармливать считываемые строки функции xml_parse:

<?
while ($data = fgets($fp)) {
  if (!xml_parse($xml_parser, $data, feof($fp))) {
    echo "<br>XML Error: ".xml_error_string(xml_get_error_code($xml_parser));
    echo " at line ".xml_get_current_line_number($xml_parser);
    break;
  }
}
?>

Здесь заметим две очень важные вещи. Первая - это то, что функции xml_parse в третьем параметре нужно передать флаг считывания последней строки (true - если строка последняя, false - если нет). Второе - это то, что как и в любом деле, мы должны следить здесь за ошибками. За это отвечают функции xml_get_error_code и xml_error_string. Первая функция получает код ошибки, а вторая - по полученному коду возвращает текстовое описание ошибки. Что в результате возникновения ошибки получится - рассмотрим позже. Не менее полезная функция xml_get_current_line_number скажет нам номер текущей обрабатываемой строки в файле.

И как всегда мы должны освободить занимаемые системой ресурсы. Для парсинга XML - это функция xml_parser_free:

xml_parser_free($xml_parser);

Вот, основные функции мы рассмотрели. Пора бы посмотреть их на деле. Для этого я придумал xml-файл с очень простой структурой:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <info who="моя">
    <address ulica="моя улица!!" kvartira="12" dom="15">123</address>
    <phone>+71234567890</phone>
  </info>
</root>

Назовем этот файл data.xml и попытаемся его распарсить с помощью следующего кода:

<?
function startElement($parser, $name, $attrs) {
    global $depth;
       
    echo str_repeat(" ", $depth * 3); // отступы
    echo "<b>Element: $name</b><br>";      // имя элемента
       
    $depth++; // увеличиваем глубину, чтобы браузер показал отступы
       
    foreach ($attrs as $attr => $value) {
        echo str_repeat(" ", $depth * 3); // отступы
        // выводим имя атрибута и его значение
        echo 'Attribute: '.$attr.' = '.$value.'<br>';
    }
}

function endElement($parser, $name) {
    global $depth;
       
    $depth--; // уменьшаем глубину
}

$depth = 0;
$file  = "data.xml";

$xml_parser = xml_parser_create();

xml_set_element_handler($xml_parser, "startElement", "endElement");

if (!($fp = fopen($file, "r"))) {
    die("could not open XML input");
}

while ($data = fgets($fp)) {
    if (!xml_parse($xml_parser, $data, feof($fp))) {
        echo "<br>XML Error: ";
        echo xml_error_string(xml_get_error_code($xml_parser));
        echo " at line ".xml_get_current_line_number($xml_parser);
        break;
    }
}
   
xml_parser_free($xml_parser);
?>

В результате разработанного нами простейшего скрипта браузер вывел в свое окно следующую информацию:

Element: ROOT
   Element: INFO
      Attribute: WHO = моя
      Element: ADDRESS
         Attribute: ULICA = моя улица!!
         Attribute: KVARTIRA = 12
         Attribute: DOM = 15
      Element: PHONE

Попробуем испортить XML-файл, заменив тег <phone> на <telephone>, а закрывающий тег оставив прежним:

Element: ROOT
   Element: INFO
      Attribute: WHO = моя
      Element: ADDRESS
         Attribute: ULICA = моя улица!!
         Attribute: KVARTIRA = 12
         Attribute: DOM = 15
      Element: TELEPHONE

XML Error: Mismatched tag at line 5

Ух ты! Сообщения об ошибках работают! Причем довольно информативные.

Эх, я забыл еще одну вещь… Мы же не вывели текст, содержащийся внутри тегов address и phone. Исправляем наш недочет - добавляем текстовый обработчик с помощью функции xml_set_character_data_handler:

xml_set_character_data_handler($xml_parser, ‘stringElement’);

И добавляем в код саму функцию-обработчик:

<?
function stringElement($parser, $str) {
    if (strlen(trim($str)) > 0) {
        global $depth;
           
        echo str_repeat(" ", $depth * 3); // отступ
        echo 'String: '.$str.'<br>'; // выводим строку
    }
}
?>

Посмотрим теперь на вывод:

Element: ROOT
   Element: INFO
      Attribute: WHO = моя
      Element: ADDRESS
         Attribute: ULICA = моя улица!!
         Attribute: KVARTIRA = 12
         Attribute: DOM = 15
         String: 123
      Element: PHONE
         String: +71234567890

О! Теперь вывели все!

Кстати, кто-нибудь заметил, что имена тегов и атрибутов все большими буквами написаны? Странно… они же в нашем xml-файле малыми буквами обозначены. Видимо где-то какие-то настройки установлены, чтобы делать uppercase…

Ааа, нашел! Оказывается есть еще функция xml_parser_set_option:

xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);

Таким вызовом мы отменяем вывод имен атрибутов и имен тегов большими буквами:

Element: root
   Element: info
      Attribute: who = моя
      Element: address
         Attribute: ulica = моя улица!!
         Attribute: kvartira = 12
         Attribute: dom = 15
         String: 123
      Element: phone
         String: +71234567890

В этой статье мы рассмотрели самый простой, но для большинства задач достаточный метод вытаскивания информации из XML-файлов. Я еще слышал про какие-то другие более мощные методы, но их буду рассматривать, когда сам изучу немного :)





Читайте также:



84 Ответов на “Как сделать xml parser.”

  1. По мне так куда проще работать со стандартным DOM (для php5, DOM XML для php4), кроме всего прочего с ним отлично работает XSL (для php5, XSLT для php 4)

  2. Боюсь вы не сделали XML parser, а просто воспользовались готовым. 😉

    PS: SAX вообще говоря довольно интересный инструмент для того чтобы обрабатывать большие документы и не разбазаривать память на построение DOM, но почему-то о нем не все помнят.

  3. novice

    В основе механизма SAX лежит принцип поточной обработки xml. Он как раз используется для обработки больших файлов. Не используют его в таких случаях только по неопытности думаю 😉

  4. Z

    Простите, а это “с нуля” или Вы чей-то скрипт разобрали?

  5. novice

    Конечно с нуля. Я же просто использовал базовые возможности. Грех не написать с помощью них парсер самому.

  6. Антон

    Может кто-нибудь знает наилучший способ парсинга документов с ошибками на подобие: Mismatched tag. А то в интернете страницы без ошибок - это редкое исулючение…

  7. Игорь

    гут статья)

  8. Андрей

    У меня возникла проблема с русскими символами в xml файле. PHP не хочет их парсит, вылетая с ошибкой:
    Element: ROOT
    XML Error: Invalid character at line 3

    Подскажите пожалуйста что делать.

  9. novice

    Андрей, попробуйте сохранить xml-файл в кодировке UTF-8 без сигнатуры (without BOM).

  10. nika

    у меня не работает вывод String куда именно нужно вставить последнюю функцию, помогите пожалуйста?

  11. novice

    nika, Вам нужно вставить вызов
    xml_set_character_data_handler($xml_parser, ’stringElement’);
    после
    xml_set_element_handler($xml_parser, “startElement”, “endElement”);

  12. nika

    да и еще такой вопрос а что нужно сделать, чтобы русские буквы отображались нормально, а не “кракозябрами”

  13. novice

    nika, попробуйте сохранить xml-файл в кодировке UTF-8 без сигнатуры (without BOM)

  14. nika

    novice, у меня “кракозябры” при выводе уже запарсенного файла

  15. novice

    В общем для правильного отображения нужно соблюдать следующее:
    1. нужно, чтобы xml-файл был в кодировке UTF-8
    2. в xml-файле первая строчка должна быть такой:
    < ?xml version="1.0" encoding="UTF-8"?>
    3. нужно инициализировать парсер так:
    xml_parser_create(“UTF-8″);
    4. нужно перед выводом содержимого xml-файла в браузер настроить последний на кодировку UTF-8:
    header( “Content-Type: text/html; charset=utf-8″);

  16. nika

    а куда именно вставить вот эту строчку
    header(“Content-Type: text/html; charset=utf-8″);
    я поставила в начале всех функций, но выдается ошибка

  17. novice

    Эту строчку нужно вставить перед выводом информации на экран. А какая ошибка выдается?

  18. Olga

    Спасибо огромное за парсер!!!
    нашла ошибочку ’stringElement’ сделайте в тексте с двойными кавычками - а то у меня вывод String тоже не сразу заработал :)) ещё раз спасибо!

  19. novice

    Тут просто нужно быть аккуратнее с кавычками. ’stringElement’, `stringElement` и ‘stringElement’ - не одно и то же с точки зрения PHP

  20. nika

    novice, у меня вылетает вот такая ошибка Warning: Cannot modify header information - headers already sent by (output started at Z:\home\parser\www\index.php:9) in Z:\home\parser\www\index.php on line 10
    10 линия это как раз header
    а на 9 начинается php, то есть вот такой код: <?php

  21. Спасибо большое! Сейчас столкнулся с подобной проблемой (нужно распарсить базу 1С-ки и перегнать её в удобочитаемый формат для своей CMS-ки), а у вас все просто и понятно расписано! :)

    p.s.: а еще спасибо за урок по сокетам!

  22. novice

    Пожалуйста 😉

  23. Рома

    Отличная статья. Спасибо

  24. Mark

    Статья класс, побольше бы таких. Кто знает как теперь это всё добавить в базу данных, каждый элемент, атрибут в разные столбцы?

  25. Андрей

    Вот я как раз тоже озадачен такой проблемой импорт из xml в php… нарою что нить отпишу

  26. Mark

    Заранее большое спасибо. Кстати я сделал с помощью XMLReader но добавляет 200.000 записей на localhost примерно 30 минут по идее за 3 минуты должно.

  27. novice

    Mark, попробуйте ради эксперимента сделать то же самое на SimpleXML или с помощью другого PHP-инструмента

  28. Константин

    Спасибо, Вы указали направление.

  29. Имя

    ага, и попробуйте потом с simplexml, безгеморно, например из

    Просто Текстовая нода, а это ещё одна текстовая
    нода с сылкой

    вытищить нотацией -> или [] значения нод и , и особенно, значения их аттрибутов.
    повторяю - безгеморно 😉

    так что SAX, рулит и не только для док-тов не больше 5 МБ, а так же и для всех тех, которые имеют так называемый “смешанный-XML контент”

  30. Имя

    мдя… мой вам совет - прикрутите парсинг ввода пользователя 😉

  31. mat85

    Для отображения текста на русском языке используем
    iconv(“UTF-8″,”windows-1251″, $attr), где $attr, то что нужно вывести.

  32. natureALL

    Спасибо, хорошая статья. У меня получилось всё и сразу. Дальше надо делать выборки из списка. На эту тему бы статейку, а? Например, есть группы товаров. Выводим только группы. Или список внутри группы.

  33. novice

    Для этого, как мне кажется, лучше воспользоваться SimpleXML. Там удобнее будет связи соблюдать. А насчет статьи - может как-нибудь напишу 😉

  34. SoTKoM

    Спасиб, хорошая статья!!!!
    Вот еще бы статейку наподобие, только как редактировать xml с php!

  35. Arthur

    Есть такие два класса интересных в стандартной библеотеке PHP5 - DOMDocument() и XSLTProcesor() использовал на довольно крупных проектах - не жалуюсь =)

  36. спасибо! полезная статейка! мне сейчас особенно нужно было решение как парсить xml с помощью php, поэтому статейка пригодилась! спасибо!

  37. Есть такая проблема. При выводе String длинной более 174 символов происходит разбиение текста на несколько String(по 174). Где может быть данное ограничение и как его преодолеть?

  38. Сергей

    Очень помогло. Автору респект.

  39. mobon

    Отличная статья очень помогла. Давно искал такой парсер!
    Кладу в капилку денюжку

  40. novice

    mobon, спасибо! :)

  41. nKognito

    Вообще у PHP есть еще одна библиотечка для разбора XML по принципу SAX - называется XMLReader.

  42. У меня парсер выдает ошибку:

    Elemet: ROOT

    XML Error: Invalid character at line 3

    вот

  43. novice

    RuFF, скорее всего Вы обрабатываете XML-файл, сохраненный не в UTF-8 кодировке. На третьей строке парсер находит символы, которые не может разобрать, а это означает, что файл в неверной кодировке.

  44. Andrey

    Народ, а что дальше то делать с тем, что получилось ? =)
    Мне вот нужно достать телефон +71234567890, как мне это сделать ?

  45. Andrey, Вам нужно редактировать функцию stringElement, там ведь есть $str - это и есть нужная переменная

  46. Спасибо, очень просто и легко приспособить к своей задаче.
    В.Гуман

  47. Dimon EBURG

    Да, пасибо, действительно помогло решение.

  48. SAX-парсер, конечно хорошая штука, так как позволяет обрабатывать XML-файлы огромных размеров, однако, при этом, работает очень медленно.
    Поэтому, в большинстве случаев, чем морочиться с SAX парсером - значительно проще было бы применить SimpleXML - отличное решение для небольших XML-файлов.

  49. Andrewg

    ребят, а как теперь получить переменные в php из этих данных, чтобы их использовать далее. Или можно создать массив переменных, спасибо

  50. vbv

    Господа, как вывести из xml с помощью simplexml не стандартные теги, например ?
    Переменная вида $text = $item->yandex:full-text не работает.

  51. Все ОК, только обратите внимание на наличие следующего параметра,

    function cdata($parser, $cdata)
    {
    var_dump($parser, $cdata);
    }

    xml_set_character_data_handler($this->parser, “cdata”);

    Без него не хочет парсить XML с CDATA…. размером в 1.5 метра

  52. TonyKiedis

    спасибо, статья интересная.вот,хотелось бы спросить совету)))мне нужно парсить документ примрено размером 15-30 МБ.использовал SimpleXML и DOM -загибаются на этапе загрузки файла??все-таки лучшее решение это SAX-парсер??или модет посоветуете что-то еще?заранее спасибо)

  53. Артем

    2Nika, Функция header() работает только в том случае, если до нее не было ничего выведено, то бишь первым действие производили ее, т.к. мы отсылаем заголовки на эту страницу, которые говорят нам, что текст нужно переводить в кодировку UTF-8. Судя по вашей ошибке у вас есть пропуски строк, поэтому сделайте вот так:

    Значение == порядковый номер строки
    <?php 1
    header(”Content-Type: text/html; charset=utf-8?); == 2

  54. Arch

    Отлмчно я доволен описанием и комметнариями. Однако кто-нибудь может ответить как забрать длянные из таблицы вставленной в xml файлб к примеру:

    RSS NBG Currency Rates
    Currency Rates
    Mon, 02 Nov 15:18:28 +0400

    <![CDATA[
    AED
    10 Драхм ОАЭ
    4.5714
    0.0000

    AMD
    1000 Армянских Драм
    4.3465
    0.0000

    AUD
    1 Австралийский Доллар
    1.5354
    0.0000

    AZN
    1 Азербайджанский манат
    2.0904
    0.0000
    ]]>

  55. Grass-snake

    Статья супер)
    Если можно, пожалуйста, пришлите php исходник на . Спасибо)

  56. Спасибо за пост, помог освежить память :)

    Очень понравилось, в каком стиле написано (“Ааа, нашел! :)” и такое вот).

    Успехов! 😉

  57. Макс

    у меня не выводится параметр string:. делал вроде все как написано. остальное работает. параметр стринг в моей задаче не только цифренный но и буквенный

  58. Tim

    подскажите, как можно осуществить вывод только определенных значений sting

  59. Спасибо за парсер.

  60. Михаил

    а как с $value вытащить каждое значения в переменные?

  61. Ринат

    Статья супер! Рахмет за инфу я до этого даже и не знал что такое парсинг! Очень помогло при интеграции xml в iframe который был вставлен php!
    Автор молодец!

  62. Ринат

    Статья супер! Рахмет за инфу я до этого даже и не знал что такое парсинг! Очень помогло при интеграции xml в iframe который был вставлен php!
    Автор молодец! +5

  63. Статья повторяется на другом сайте
    Но всеже она мне помогла

  64. Вот, почитал я эту статью и написал себе хороший парсер. Спасибо.

  65. Nik

    Спасибо тебе за статью! Очень помогло разобраться с нуля в парсинге XML средствами PHP.

  66. Спасибо за статью. Скрипт - хорошая основа, для более серьёзных парсеров!

  67. nahuimaeimj

    хуйня какая-то. ничего не работает

  68. Дмитрий

    Чувак, у тебя самое реальное объяснение про парсинг хмл!
    я целый час тупил и ниче не мог понять, ходил на пхп.нет ОДНАКО
    когда по-русски объясняют что да как, это гораздо ускоряет
    восприятие! Блоги рулят! :)

  69. Всё намного проще делается в действительности
    yurban.ru/development/php_xml_parser

  70. Micha

    YURBAN +1
    у вас всё просто ясно и понятно)

  71. Да ну и парсер! на dom document проще делать)))

  72. Подскажите как правильно обратиться к функции?
    Имеется результат работы скрипта:
    Element: CMT
    String: 0-8091223 @ 10/15/2011 07:53:15 PM
    Element: DESC
    String: Mixovka.Meeting travelers. SPOT-Making travels more secure.

    Мне нужно:
    if(имя элемента = чему-то){делаем это}
    if(имя элемента = чему-то другому){делаем другое}

    переменную $name функция function stringElement($parser, $str) не видит в упор

  73. Как сделать xml parser (PHP). | Articles

    […] Как сделать xml parser. […]

  74. […] Как сделать xml parser. […]

  75. написал подобный класс
    dpyatkov.ru/2012/01/12/simplexmlreader/ - симбиоз simplexml и xmlreader
    может кому пригодится

  76. Спасибо.
    У Вас очень хороший подход.
    Побольше бы таких толковых спецов.

    Ясный ум.

  77. Павел

    Данный метод не годится. Попробуйте в поле
    Залить длинный текст с русскими и латинскими буквами (кодировка естественно в UTF-8). Строка будет разрезана на несколько частей.
    Для примера вот такая строка разрезалась на 4 части

    START Русский текст UTF8 The optional encoding specifies the character encoding for the input/output in PHP 4. Starting from PHP 5, the input encoding is automatically detected, so that the encoding parameter specifies only the output encoding. In PHP 4, the default output encoding is the same as the input charset. If empty string is passed, the parser attempts to identify which encoding the document is encoded in by looking at the heading 3 or 4 bytes. In PHP 5.0.0 and 5.0.1, the default output charset is ISO-8859-1, while in PHP 5.0.2 and upper is UTF-8. The supported encodings are ISO-8859-1, UTF-8 and US-ASCII END

  78. <srcipalert(“opa”)t></script>

  79. alert()

  80. Albert

    Подскажите, пожалуйста, как при помощи SAX парсера php нарисовать, допустим, прямоугольник. Координаты прямоугольника хранятся в xml файле в виде аттрибутов элемента. К примеру:

  81. Евген

    А подскажите где можно поискать пхп файл что бы смог работать с файлом хмл большого размера в общем есть файл хмл содержащий в себе программу передач на неделю порядка 150 каналов , мне нужно что бы пхп выводил мне тв программу на сайте .

  82. Владислав

    Спасибо за статью! Полезно.

  83. Олег

    А для чего защита от копирования? Ты показываешь парсер для выдрыпона, или чтобы люди пользовались? Тогда лучше бы и не выпендривался. Половина уйдет с твоего сайта

  84. Каша

    Олег, отключи JS в браузере и копипасть себе на здоровье


© Copyright. . I-Novice. All Rights Reserved. Terms | Site Map