...

Программы парсинга xml что это

Введение¶

Парсинг — это извлечение необходимых данных и сохранения их в удобном виде для последующего использования — поиска и анализа.

XML — это формат хранения и представления данных, который очень широко используется в самых различных областях:

  • любые сайты Интернет (в том числе и этот) показывают Вам информацию, сохраненную в формате xml
  • документы и электронные таблицы, с которыми Вы работаете на компьютере, электронные книги которые Вы читаете хранят данные в формате xml,
  • архивные статистическое данные госзакупок, биржевых котировок, научных исследований активно используют xml для хранения

Ознакомиться со списком примеров полезных и интересных данных, хранящихся в формате xml можно здесь.

Зачем нужен парсинг данных?¶

Данные в формате xml удобно хранить, но не удобно регулярно использовать, т.е. регулярно выполнять поиск по коллекции данных, их преобразование и анализ. Для этого, необходимые данные извлекаются из xml формата (парсятся) и сохраняются в более удобной форме, например, в базе данных.

Алгоритм парсинга источников xml¶

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

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

  1. анализируется структура источника xml в котором находятся нужные данные: определяются пути доступа к данным — в каких узлах или атрибутах они хранятся, и их структура — данные являются неделимыми (атомарными) или составными, к какому типу они принадлежат- строковому, логическому, числовому. Этот этап выполняется программой-парсером в полуавтоматическом режиме. От набора инструментов, предлагаемых программой-парсером и удобства работы с ними зависят время, которое Вы затратите на анализ структуры xml и качество извлекаемых программой-парсером данных — их полноту и точность.
  2. непосредственно извлечение данных и их сохранение в формате, удобном для последующего поиска, трансформации и анализа.Возможно, таким форматом будет база данных, а возможно — таблица Exel или что нибудь еще.

Требования к программе-парсеру¶

Практика работы с источниками данных xml помогла сформировать ряд практических требований которым должна удовлетворять программа-парсер

  1. программа должна уметь извлекать данные из различных типов источников xml — как архивных файлов на компьютере, так и расположенных в сети: страниц сайтов, новостных лент, результатов API запросов и т.д..
  2. программа должна уметь извлекать данные из файлов большого размера. Многие полезные наборы данных хранятся в файлах размером несколько гигабайт.
  3. программа должна иметь удобные инструменты для анализа xml источников с неизвестной структурой. Эти инструменты должны предоставлять информацию как в графическом так и консольном вариантах.
  4. Настройка программы для извлечения данных должна быть максимально простой и не требовать от пользователя углубленных знаний структуры xml формата. Т.е., для того, чтобы извлечь данные, Вам не нужно, например, знать, что такое xpath и как его применять.
  5. программа должна работать на компьютерах с различными операционными системами (windiws, *nix), быть нетребовательной к ресурсам системы (работать даже на маломощных компьютерах) и поддерживать как локальное так и удаленное управление
  6. Программа должна предоставлять инструменты для сохранения извлеченных данных во все востребованные форматы.

К сожалению к моменту написания документации, программы-парсера, удовлетворяющей перечисленным требованиям мы не нашли, поэтому написали свою.

Итак, представляем инструмент парсинга xml и html — xmlPyParser

Оглавление

  • Введение
    • Что такое парсинг
    • Зачем нужен парсинг данных?
    • Алгоритм парсинга источников xml
    • Требования к программе-парсеру

    Xml парсер

    Xml парсер — это программа, которая извлекает из исходного файла xml формата данные и сохраняет или использует для последующих действий.

    Почему нужны xml парсеры?

    xml парсер - пример исходного файла

    xml парсер — пример исходного файла

    В первую очередь потому что сам по себе формат xml популярный среди компьютерных стандартов. XML файл выглядит так:

    т.е. по сути есть теги, есть какие-то правила какие теги должны следовать друг за другом.

    Причина популярности xml файлов заключается в том, что он хорошо читаем человеком. И то, что его относительно легко обрабатывать в программах.

    Минусы xml-файлов.

    xml парсер - большой файл xml

    xml парсер — большой файл xml

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

    Как пишутся XML парсеры?

    Парсеры пишутся на языках программирования. Как говорится пишутся на всех , но не некоторых больше. Следует понимать, что есть языки программирования, в которых есть уже встроенные библиотеки для парсинга xml файлов. Но в любом случае даже если библиотеки нет, можно всегда найти подходящую библиотеку для этого дела и использовать ее для извлечения данных из файла.

    Глобально есть 2 разных подхода как парсить xml файлы.

    Первый — это загружать xml файл полностью в память ну и дальше делать манипуляции по извлечению данных.

    Второй — это потоковый вариант. В этом случае в языке программирования определяется определенные теги на которые нужно реагировать функциям создаваемого xml парсера и уже программист сам решает, что нужно делать в случае обнаружения того или иного тега.

    Плюсом первого подхода является скорость. Сразу все загрузил файл, потом быстро пробежался в памяти нашел , что нужно и самое главное программировать легко. но Минус есть и очень важный — это

    большой объем памяти требуется для работы. Иногда, я бы даже сказал часто бывает так, что просто невозможно обработать и распарсить xml файл, т.е. создать xml парсер, чтобы работал по первому способу корректно. Почему так? Ну, например, ограничение для 32 битных приложений под виндой позволяет программе максимально занимать 2 гигабайта памяти — больше нельзя.

    Однако программировать по поточному варианту сложно. Сложность при достаточно серьезном извлечении вырастает в разы, что соответственно сказывается и на сроках и на бюджете.

    Валидность xml файлов и парсеров.

    xml парсер - невалидный xml файл

    xml парсер — невалидный xml файл

    Все было бы с xml файлами и xml парсерами хорошо, но вот есть проблема. Ввиду того, что создать xml файл может «любой школьник», а в реальности так и есть (потому что очень много кода пишется школьниками, то появляются невалидные файлы , т.е. некорректные. Что это значит и чем это чревато? Самая большая проблема, это то, что просто невозможно иногда корректно распарсить невалидный файл. Например у него теги не закрываются как следовало бы ожидать по стандарту или например кодировка задана неверно. Другая проблема заключается, что если например делаешь парсер на .net то, можно создать так называемые врапперы, и самое обидное бывает, что вот сделаешь такой враппер, а потом считываешь им файл, который «школьник» создал , а файл невалидный и его прочитать невозможно. Поэтому приходиться изгаляться и прибегать к весьма и весьма непопулярным вариантам парсинга таких файлов. А все из=за того, что многие создают xml файлы без использования стандартных библиотек и с полным отвращениям ко всем стандартам xml файлов. Заказчикам это сложно объяснить. Они ждут результат — xml парсер, который преобразует данные из оригинального файла в другой формат.

    Как создавать xml парсеры (первый вариант)

    xml парсер - язык запросов xpath

    xml парсер — язык запросов xpath

    Есть такой язык запросов к XML данным как Xpath. Язык этот имеет две редакции, углубляться не будем в особенности каждой версии. Лучше представление про этот язык покажут примеры того как использовать его для извлечения данных. Например.

    //div[@class=»supcat guru»]/a[contains(@href, ‘catalog.xml?hid=’)]

    что делает этот запрос. Он забирает все а тэги, которые имею хреф, содержаший текст catalog.xml?hid= и этот а тэг должен быть чайлдом дива у которого класс равен supcat guru.

    Методы работы с «тяжёлыми» XML

    image

    На работе попросили провести исследование какими средствами лучше разбирать объёмный XML файл (более 100Mb). Предлагаю сообществу ознакомиться с результатами.

    Рассмотрим основные методы работы с XML:

    Simple XML

    Минусы: работает очень медленно, собирает весь файл в память, дерево составляется в отдельных массив.
    Плюсы: простота работы, работа «из коробки» (требует библиотеки libxml которая включена практически на всех серверах)

    Пример использования Simple XML

    $xml = simplexml_load_file("price.xml"); echo "\n"; foreach ($xml->xpath('/DocumentElement/price') as $producs) < ?>  echo "
    name; ?> company; ?> city; ?> amount ?>
    \n";

    DOM

    Минусы: работает очень медленно, как и все предыдущие примеры собирает весь файл в память.
    Плюсы: На выходе привычный DOM с которым очень легко работать.

    Пример использования DOM

    $doc = new DOMDocument(); $doc->load( 'books.xml' ); $books = $doc->getElementsByTagName( "book" ); foreach( $books as $book ) < $authors = $book->getElementsByTagName( "author" ); $author = $authors->item(0)->nodeValue; $publishers = $book->getElementsByTagName( "publisher" ); $publisher = $publishers->item(0)->nodeValue; $titles = $book->getElementsByTagName( "title" ); $title = $titles->item(0)->nodeValue; echo "$title - $author - $publisher\n";

    xml_parser и XMLReader.

    Предыдущие 2 нам не подходят из-за работы с целым файлом, т.к. файлы у нас бывают по 20-30 Mb, и во время работы с ними некоторые блоки образуют цепочку (массив) в 100> Mb

    Оба способа работают чтением файла построчно что подходит идеально для поставленной задачи.

    Разница между xml_parser и XMLReader в том что, в первом случае вам нужно будет писать собственные функции которые будут реагировать на начало и конец тэга.

    Проще говоря, xml_parser работает через 2 триггера – тэг открыт, тэг закрыт. Его не волнует что там идёт дальше, какие данные используются и т.д. Для работы вы задаёте 2 триггера указывающие на функции обработки.

    Пример работы xml_parser

    class Simple_Parser < var $parser; var $error_code; var $error_string; var $current_line; var $current_column; var $data = array(); var $datas = array(); function parse($data) < $this->parser = xml_parser_create('UTF-8'); xml_set_object($this->parser, $this); xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1); xml_set_element_handler($this->parser, 'tag_open', 'tag_close'); xml_set_character_data_handler($this->parser, 'cdata'); if (!xml_parse($this->parser, $data)) < $this->data = array(); $this->error_code = xml_get_error_code($this->parser); $this->error_string = xml_error_string($this->error_code); $this->current_line = xml_get_current_line_number($this->parser); $this->current_column = xml_get_current_column_number($this->parser); > else < $this->data = $this->data['child']; > xml_parser_free($this->parser); > function tag_open($parser, $tag, $attribs) < $this->data['child'][$tag][] = array('data' => '', 'attribs' => $attribs, 'child' => array()); $this->datas[] =& $this->data; $this->data =& $this->data['child'][$tag][count($this->data['child'][$tag])-1]; > function cdata($parser, $cdata) < $this->data['data'] .= $cdata; > function tag_close($parser, $tag) < $this->data =& $this->datas[count($this->datas)-1]; array_pop($this->datas); > > $xml_parser = new Simple_Parser; $xml_parser->parse('test');

    В XMLReader всё проще. Во первых, это класс. Все триггеры уже заданы константами (их всего 17), чтение осуществляется функцией read() которая читает первое вхождение подходящее под заданные триггеры. Далее мы получаем объект в который заносится тип данных (аля триггер), название тэга, его значение. Также XMLReader отлично работает с аттрибутами тэгов.

    Пример использования XMLReader

     reader->name == $name && $this->reader->nodeType == XMLReader::ELEMENT) < $result = array(); while (!($this->reader->name == $name && $this->reader->nodeType == XMLReader::END_ELEMENT)) < //echo $this->reader->name. ' - '.$this->reader->nodeType." - ".$this->reader->depth."\n"; switch ($this->reader->nodeType) < case 1: if ($this->reader->depth > 3 && !$ignoreDepth) < $result[$nodeName] = (isset($result[$nodeName]) ? $result[$nodeName] : array()); while (!($this->reader->name == $nodeName && $this->reader->nodeType == XMLReader::END_ELEMENT)) < $resultSubBlock = $this->parseBlock($this->reader->name, 1); if (!empty($resultSubBlock)) $result[$nodeName][] = $resultSubBlock; unset($resultSubBlock); $this->reader->read(); > > $nodeName = $this->reader->name; if ($this->reader->hasAttributes) < $attributeCount = $this->reader->attributeCount; for ($i = 0; $i < $attributeCount; $i++) < $this->reader->moveToAttributeNo($i); $result['attr'][$this->reader->name] = $this->reader->value; > $this->reader->moveToElement(); > break; case 3: case 4: $result[$nodeName] = $this->reader->value; $this->reader->read(); break; > $this->reader->read(); > return $result; > > public function parse($filename) < if (!$filename) return array(); $this->reader = new XMLReader(); $this->reader->open($filename); // begin read XML while ($this->reader->read()) < if ($this->reader->name == 'store_categories') < // while not found end tag read blocks while (!($this->reader->name == 'store_categories' && $this->reader->nodeType == XMLReader::END_ELEMENT)) < $store_category = $this->parseBlock('store_category'); /* Do some code */ $this->reader->read(); > $this->reader->read(); > > // while > // func > $xmlr = new StoreXMLReader(); $r = $xmlr->parse('example.xml'); 

    Тест производительности

    Код генератора example.xml

    openMemory(); $xmlWriter->startDocument('1.0', 'UTF-8'); $xmlWriter->startElement('shop'); for ($i=0; $istartElement('product'); $xmlWriter->writeElement('id', $productId); $xmlWriter->writeElement('name', 'Some product name. ID:' . $productId); $xmlWriter->endElement(); // Flush XML in memory to file every 1000 iterations if (0 == $i%1000) < file_put_contents('example.xml', $xmlWriter->flush(true), FILE_APPEND); > > $xmlWriter->endElement(); // Final flush to make sure we haven't missed anything file_put_contents('example.xml', $xmlWriter->flush(true), FILE_APPEND);

    Результаты тестирования (чтение без разбора данных)

    Характеристики тестовой среды
    Ubuntu 16.04.1 LTS
    PHP 7.0.15
    Intel® Core(TM) i5-3550 CPU @ 3.30GHz, 16 Gb RAM, 256 SSD

    Метод Время выполнения (19 Mb) Время выполнения (190 Mb)
    Simple XML 0.46 сек 4.56 сек
    DOM 0.52 сек 4.09 сек
    xml_parse 0.22 сек 2.25 сек
    XML Reader 0.26 сек 2.18 сек

    P.S. Советы и комментарии с удовольствием выслушаю. Прошу сильно не пинать
    При подготовке материала использовались источники:
    https://textminingdocumentation.readthedocs.io/ru/latest/article/xml_parsing.html
    https://catalogloader.com/xml-parser.html
    https://habr.com/ru/articles/330240/

Оцените статью