Как распарсить большой xml файл

Обновлено: 04.07.2024

Некоторое время назад была задача разбора гигабайтных размеров XML-файлов на PHP. Попробуем поискать нужные библиотеки:

SimpleXML
Первая мысль воспользоваться SimpleXML, однако её принцип работы основан на чтении всего файла целиком и разворачивании DOM-дерева в памяти. Очевидно такой код упрется в memory_limit.

DOM
Ситуация по использованию памяти такая же как и SimpleXML.

Запись опубликована в рубрике Без рубрики с метками php. Добавьте в закладки постоянную ссылку.

24 комментария на «Простая обработка больших XML файлов»

Спасибо. 🙂 Сейчас попробую.

Отлично! Правда пытаюсь отловить один баг. Почему-то после парсинга 100 объетов из большого xml-файла мой локальный сервер продолжает грузить проц. Не сталкивались?

Решил проблему. Если кому-то будет полезно:


$file = "example1.xml";
$reader = new ExampleXmlReader1();

$reader->open($file);
$reader->parse();
$arrayErrors = libxml_get_errors();
$reader->close();

$xml_errors = "";
foreach ($arrayErrors as $xmlError) $xml_errors .= $xmlError->message;
if ($xml_errors != "") echo "XML not valid: ".$xml_errors;
>

Хотел поробовать, не разобрался что к чему:(

Спасибо! Помогла ваша библиотека!


set_time_limit(0);
error_reporting(E_ALL ^ E_NOTICE ^ E_DEPRECATED);

class parse_xml extends SimpleXMLReader public function __construct() <>

public function getAll() $xml = $this->expandSimpleXml();
$attr = (array)$xml->attributes();
$attr = $attr['@attributes'];
$value = (string)$xml;
return array($xml, $value, $attr);
>
>

$parser = new parse_xml();
$parser->registerCallback('yml_catalog', function($render) list(,, $attr) = $render->getAll();

$this->ozon_update = $attr['date'];
return true;
>);
$parser->registerCallback('offer', function($render) list($item, $value, $attr) = $render->getAll();
$ozon_title = parse_filter($item->name);
>);
$parser->open("./ozon_book.xml");
$parser->parse();
$parser->close();

Поделюсь своими наблюдениями. При таком конфиге процесс запущенный в консоли умирает где-то через 2-3 минуты. Умирает как на 200мегабайтном так и на 2 гигабайтном файле примерно одинаково. На значительно меньших файлах работает шустро и удобно. Колбеки понравились.

$ time php ./parse_ozon.php parse
Убито

real 2m19.644s
user 0m18.802s
sys 0m2.649s

Умирает без отработки колбеков, значит модель SAX не работает.

Олег, sax работает, просто ошибочное использование библиотеки.

Если необходимо читать атрибуты корневого тега, не вызывайте expand-функций, лучше так:
$reader->getAttribute("date")

Для обработки всех вложенных узлов верным будет использовать примерно так:
$parser->registerCallback('/yml_catalog/shop', function($render)< >);

Спасибо за комментарий.

А почему /yml_catalog/shop будет более верным вариантом? Как это спасёт от всё тех же операций по проходу всего файла ведь он из одних шопов по сути и состоит.

Класс SimpleXmlReader ничего нового не придумывает, только расширяется стандартные XMLReader до более удобного API.

Про чтение корневого узла

$parser->registerCallback('yml_catalog', function($render) $reader->getAttribute('date'); // так можно
$reader-> expandSimpleXml(); // а так скушает памяти много
>);

Чтож, сработало. Спасибо. Теперь задумываюсь о том как заставить его бить файлы на чанки и парсить паралельно в несколько потоков. Есть мысли?:)

XML является популярным форматом для обмена данными. Go поддерживает библиотеку для парсинга XML файлов. Процесс похож на работу с JSON. Обычно используется структура, что соответствует схеме XML, и с помощью этого сразу проводится парсинг содержимого XML. Проблема возникает, когда XML слишком большой для имеющейся памяти, поэтому нужно обрабатывать файл по кускам. В данной инструкции показано, как работать с крупными XML файлами и собрать требуемую информацию.

Как выполнить парсинг большого XML файла в Golang?

1. Создайте файл data.xml со следующим содержимым XML:


Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎

Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.

< description > An in - depth look at creating applications < title > Visual Studio 7 : A Comprehensive Guide < / title > < description > Microsoft Visual Studio 7 is explored in depth ,

2. Создайте файл xml.go со следующим содержимым:

3. Запустите код через go run xml.go ;
4. Посмотрите на результат в терминале:

xml golang

Пакет xml для парсинга XML файлов в Go

С помощью функции NewDecoder из пакета xml создается декодер для содержимого XML файла.

Через вызов метода Token для Decoder мы принимаем xml.Token . xml.Token является интерфейсом, что содержит тип токена. Поведение кода можно определить на основании типа. Код из примера выше проверяет, если парсируемый изначальный элемент xml.StartElement является одним из элементов <book> . Затем производится парсинг данных по частям в структуру Book . В таком случае позиция указателя в базовом Reader в Decoder перемещается через структуру данных, и парсинг может продолжаться.


Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.

Xml парсинг

Вы когда-нибудь сталкивались с надоедливым XML-файлом, который вам нужно проанализировать, чтобы получить важные значения? Давайте узнаем, как создать парсер Python XML.

Мы рассмотрим, как мы можем анализировать подобные XML-файлы с помощью Python, чтобы получить соответствующие атрибуты и значения.

Метод 1: Использование ElementTree (рекомендуется)

Мы можем использовать библиотеку ElementTree Python для решения этой задачи.

Это самый простой и рекомендуемый вариант для создания синтаксического анализатора Python XML, поскольку эта библиотека по умолчанию входит в состав Python.

Она не только обеспечивает легкий доступ, поскольку уже установлена, но и работает довольно быстро. Давайте посмотрим, как именно мы можем извлечь атрибуты из нашего тестового файла.

Мы будем использовать интерфейс xml.etree.ElementTree внутри основного xml пакета.

Дерево синтаксического анализатора

Давайте сначала построим корневой узел этого дерева синтаксического анализа. Это самый верхний узел, он необходим нам для начала синтаксического анализа.

К счастью для нас, в этом API уже есть следующий метод:

Это автоматически прочитает входной XML-файл и получит для нас корневой узел.

Похоже, он проанализирован. Но мы пока не можем это проверить. Итак, давайте проанализируем другие атрибуты и попробуем получить значение.

Получение значения соответствующих атрибутов

tag.get(attribute) получит значение нашего <attribute> на уровнях, на которых мы ищем. Итак, нам просто нужно сделать это в <header/type> и получить значения атрибутов <heading> и <text> . Это оно!

Мы получили все значения на этом уровне нашего дерева синтаксического анализа XML! Мы успешно проанализировали наш XML-файл.

Возьмем другой пример, чтобы все прояснить.

Теперь предположим, что XML-файл выглядит так:

Здесь мы должны не только получить значения атрибутов name , но также получить текстовые значения 10, 20, 30 и 40 для каждого элемента на этом уровне.

Чтобы получить значение атрибута name , мы можем сделать то же самое, что и раньше. Мы также можем использовать tag.attrib[name] чтобы получить значение. Это то же самое, что и tag.get(name) , за исключением того, что он использует поиск по словарю.

Получить текстовое значение просто. Просто используйте:

Итак, наша полная программа для этого парсера будет:

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

Метод 2: использование BeautifulSoup (надежный)

Это также еще один хороший выбор, если по какой-то причине исходный XML плохо отформатирован. XML может работать не очень хорошо, если вы не выполните предварительную обработку файла.

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

Чтобы установить его, используйте pip и установите модуль bs4 :

Я дам вам небольшой фрагмент нашего предыдущего XML-файла:

Я передам этот файл, а затем bs4 его с помощью bs4 .

Синтаксис аналогичен нашему модулю xml , поэтому мы по-прежнему получаем имена атрибутов, используя value = tag['attribute_name'] и text = tag.text . Точно так же, как и раньше!

Мы также проанализировали это с помощью bs4 ! Если ваш исходный XML файл плохо отформатирован, можно использовать этот метод, поскольку BeautifulSoup имеет другие правила для обработки таких файлов.

Каждый, кто написал в жизни хоть строчку кода, слышал такое выражение – не нужно изобретать велосипед, то есть, зачем писать то, что уже написано. И мы полностью согласны с этой мыслью — зачем изобретать велосипед, когда им уже можно с удовольствием пользоваться. Тема парсинга XML уже разобрана не раз, поэтому прокатимся на велосипеде парсинга XML по документам с неизвестной структурой.

В сети существует множество руководств, где авторы бодро разбирают структуру XML известного документа – решительно находят root у дерева, после чего мужественно пробегают циклом по тэгам, при этом циклы используют столько раз, сколько уровней вложенности у документа. Это все хорошо и отлично, когда мы знаем сколько раз нужно натравить цикл на очередной уровень вложенности.

Но у нас возникла задача, с которой мы еще не сталкивались, когда есть много документов XML, а документации по структуре данных нет, вообще. Нет, не та… . НЕТ вообще!. Как в таком случае, мы с помощью имеющегося опыта можем опознать структуру и глубину текущих файлов?

Данные XML хранятся в базе в текстовом виде — xml_text. Для начала считаем и опознаем то, что можно – получим root документа – он есть у всех.

from lxml import etree as ET from io import BytesIO import os byte_obj = BytesIO(xml_text.encode()) tree = ET.parse(byte_obj) root = tree.getroot()

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

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

[(0, 'ProductType', 'новый продукт'), (0, 'AgrNum', '1-YYYYYY'), (0, 'AgrDate', '22.07.2014'), (0, 'Account', None), (1, 'INN', '9900000099'), (1, 'Name', 'Компания'), (0, 'User', None), (1, 'FIO', 'Мошкин Сергей Михайлович'), (1, 'Position', 'ANALYST'), (0, 'ListOfCollateral', None), (1, 'Collateral', None), (2, 'CRMId', 'Квартиры'), (2, 'CollType', 'OSNResidential_Real_Estate'), (2, 'AssessType', 'Market'), (2, 'AssessSource', 'Int_Appraiser')]

Полученный список уже можно разносить по таблицам для формирования реляционной БД. Каждый уровень вложения — это новая таблица (наименование столбца — наименование тэга xml, значения в столбце – значения тэга xml). Так, уровень вложения 1 — это новая таблица, она связана с таблицей из данных уровня 0 через ключевое поле (внешний ключ). Каждое последующее вложение — это новая таблица, логика связи с таблицей предыдущего уровня такая же.

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

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