Зачем нужен XML при парсинге? Иногда бывает так, что сайт, который вам нужно спарсить, имеет API, с помощью которого можно получить желаемое, особо не напрягаясь. Поэтому сразу совет - перед тем, как парсить сайт, проверьте, нету ли у него API. Что такое API? Это набор функций, с помощью которых вы можете слать запрос этому сайту и получать нужный ответ. Вот этот ответ чаще всего приходит в формате XML. Поэтому давайте приступим к его изучению.
Пусть у вас есть XML. Он может быть в строке, либо хранится в файле или отдаваться по запросу к определенному URL. Пусть XML хранится в строке. В этом случае из этой строки нужно создать объект с помощью new SimpleXMLElement:
$str = "<?xml version='1.0'?>
<worker>
<name>Коля</name>
<age>25</age>
<salary>1000</salary>
</worker>";
$xml = new SimpleXMLElement($str);
Сейчас у нас в переменной $xml хранится объект с разобранным XML. Обращаясь к свойствам этого объекта можно получать доступ с содержимому тегов XML. Как именно - разберем чуть ниже. Если же XML хранится в файле или отдается по обращению к URL (что чаще всего и бывает), то следует использовать функцию simplexml_load_file
, которая делает тот же объект $xml
.
<?xml version='1.0'?>
<worker>
<name>Коля</name>
<age>25</age>
<salary>1000</salary>
</worker>
$xml = simplexml_load_file(путь к файлу или урл);
В примерах ниже наш XML хранится в файле или по URL. Пусть дан следующий XML.
<?xml version='1.0'?>
<worker>
<name>Коля</name>
<age>25</age>
<salary>1000</salary>
</worker>
Давайте получим имя, возраст и зарплату работника:
$xml = simplexml_load_file(путь к файлу или урл);
echo $xml->name; // выведет 'Коля'
echo $xml->age; // выведет 25
echo $xml->salary; // выведет 1000
Как вы видите, у объекта $xml
есть свойства, соответствующие тегам. Вы может обратили внимание, что тег <worker>
нигде не фигурирует при обращении. Это потому, что он корневой тег. Можно переименовать его, например, на <root>
- и ничего не поменяется.
Корневой тег в XML может быть только один, так же, как и тег <html>
в обычном HTML. Давайте чуть модифицируем наш XML.
<?xml version='1.0'?>
<root>
<worker>
<name>Коля</name>
<age>25</age>
<salary>1000</salary>
</worker>
</root>
// В этом случае у нас получится цепочка обращений
$xml = simplexml_load_file(путь к файлу или урл);
echo $xml->worker->name; // выведет 'Коля'
echo $xml->worker->age; // выведет 25
echo $xml->worker->salary; // выведет 1000
Пусть некоторые данные хранятся в атрибутах.
<?xml version='1.0'?>
<root>
<worker name="Коля" age="25" salary="1000">Номер 1</worker>
</root>
$xml = simplexml_load_file(путь к файлу или урл);
echo $xml->worker['name']; // выведет 'Коля'
echo $xml->worker['age']; // выведет 25
echo $xml->worker['salary']; //выведет 1000
echo $xml->worker; //выведет 'Номер 1'
Теги с дефисами. В XML разрешены теги (и атрибуты) с дефисом. В этом случае обращение к таким тегам происходит так:
<?xml version='1.0'?>
<root>
<worker>
<first-name>Коля</first-name>
<last-name>Иванов</last-name>
</worker>
</root>
$xml = simplexml_load_file(путь к файлу или урл);
echo $xml->worker->{first-name}; // выведет 'Коля'
echo $xml->worker->{last-name}; // выведет 'Иванов'
Перебор циклом. Пусть теперь у нас не один работник, а несколько. В этом случае мы можем перебрать наш объект с помощью цикла foreach.
<?xml version='1.0'?>
<workers>
<worker>
<name>Коля</name>
<age>25</age>
<salary>1000</salary>
</worker>
<worker>
<name>Вася</name>
<age>26</age>
<salary>2000</salary>
</worker>
<worker>
<name>Петя</name>
<age>27</age>
<salary>3000</salary>
</worker>
</workers>
$xml = simplexml_load_file(путь к файлу или урл);
foreach ($xml as $worker) {
echo $worker->name; // выведет 'Коля', 'Вася', 'Петя'
}
Из объекта в нормальный массив. Если вам неудобно работать с объектом, вы можете преобразовать его в нормальный массив PHP с помощью следующего хитрого приема.<
$xml = simplexml_load_file(путь к файлу или урл);
var_dump(json_decode(json_encode($xml), true));
Расширение SimpleXML предоставляет очень простой и легкий в использовании набор инструментов для преобразования XML в объект, с которым можно затем работать через его свойства и с помощью итераторов. SimpleXML присутствует в PHP начиная с версии 5.
Для наглядности, в качестве примера будем использовать XML, описывающий простой кулинарный рецепт, взятый с википедии.
<?xml version="1.0" encoding="UTF-8"?>
<recipe name="хлеб" preptime="5" cooktime="180">
<title>Простой хлеб</title>
<ingredient amount="3" unit="стакан">Мука</ingredient>
<ingredient amount="0.25" unit="грамм">Дрожжи</ingredient>
<ingredient amount="1.5" unit="стакан">Тёплая вода</ingredient>
<ingredient amount="1" unit="чайная ложка">Соль</ingredient>
<instructions>
<step>Смешать все ингредиенты и тщательно замесить.</step>
<step>Закрыть тканью и оставить на один час в тёплом помещении.</step>
<step>Замесить ещё раз, положить на противень и поставить в духовку.</step>
</instructions>
</recipe>
Прежде чем начать обрабатывать данные, их нужно сначала загрузить. Для этого достаточно использовать функцию simplexml_load_file(). Она принимает имя файла, и возвращает объект типа SimpleXMLElement. И с этим объектом уже можно будет работать.
$xml = simplexml_load_file('recipe.xml');
print_r($xml);
SimpleXMLElement Object
(
[@attributes] => Array
(
[name] => хлеб
[preptime] => 5
[cooktime] => 180
)
[title] => Простой хлеб
[ingredient] => Array
(
[0] => Мука
[1] => Дрожжи
[2] => Тёплая вода
[3] => Соль
)
[instructions] => SimpleXMLElement Object
(
[step] => Array
(
[0] => Смешать все ингредиенты и тщательно замесить.
[1] => Закрыть тканью и оставить на один час в тёплом помещении.
[2] => Замесить ещё раз, положить на противень и поставить в духовку.
)
)
)
Кроме того, существует еще и функция simplexml_load_string()
, которая берет XML не из файла, а из строки.
$str = file_get_contents('recipe.xml');
$xml = simplexml_load_string($str);
SimpleXML предоставляет очень удобный способ получения данных из XML. К примеру, для того чтобы получить какой-либо узел документа достаточно просто обратится к этому узлу по имени:
echo $xml->title; // выводит содержимое элемента <title>
Поскольку ингредиентов у нас несколько, то $xml->ingredient
будет массивом из четырех элементов. Перебрать все ингредиенты можно так:
foreach ( $xml->ingredient as $ingredient ) {
echo $ingredient . '<br/>';
}
Для того что бы получить, к примеру, третий ингредиент (теплая вода), достаточно обратиться к нему по индексу:
$xml->ingredient[2]; // элементы массивы нумеруются с нуля
Шаги приготовления step
являются дочерними для элемента instructions
, чтобы получить их, нужно сначала получить instructions
:
echo $xml->instructions->step; // выводит текст первого шага
Работать с атрибутами тоже очень легко. Они доступны как ассоциативный массив своего элемента. То есть, для того что бы получить название рецепта (атрибут name
корневого узла recipe
), достаточно написать:
echo $xml['name'];
Или, для получения количества первого ингредиента можно написать так:
echo $xml->ingredient['amount'];
Сейчас мы рассмотрели только один способ получения данных: когда нам уже известны названия узлов и атрибутов. Но случается и так, когда структура XML файла заранее не известна, но нам нужно его обработать. SimpleXML предоставляет и такую возможность.
Метод children()
возвращает список дочерних элементов. В нашем случае $xml
— корневая ветвь, и если написать:
$nodes = $xml->children();
echo $nodes[0];
то получим содержимое элемента <title>
, а если:
$nodes = $xml->children();
echo $nodes[2];
то второй ингредиент.
Обойти все дочерние ветви первого уровня легко можно при помощи цикла foreach:
echo '<ul>';
foreach ( $xml->children() as $node ) {
if ( count($node) == 0 ) echo '<li>' . $node . '</li>';
}
echo '</ul>';
Результат:
<ul>
<li>Простой хлеб</li>
<li>Мука</li>
<li>Дрожжи</li>
<li>Тёплая вода</li>
<li>Соль</li>
</ul>
Фукция count()
позволяет определить количество дочерних узлов.
Для того, чтобы получить имя текущий ветви, используется метод getName()
:
$nodes = $xml->children();
echo $nodes[0]->getName(); // выведет title
Получить список атрибутов для текущего элемента поможет метод attributes()
. По функционалу и механизму работы он аналогичен методу children()
, за тем исключением, что здесь идет работа с атрибутами.
$nodes = $xml->children();
// все атрибуты узла <ingredient>Мука</ingredient>
foreach ( $nodes[1]->attributes() as $name => $value ) {
echo 'атрибут ' . $name . ', значение ' . $value . '<br/>';
}
Результат:
атрибут amount, значение 3<br/>
атрибут unit, значение стакан<br/>
Объект SimpleXMLElement позволяет манипулировать всеми элементами:
$xml = simplexml_load_file('recipe.xml');
$xml->title = 'Ржаной хлеб';
$xml->ingredient[0] = 'Ржаная мука';
print_r($xml);
$xml->ingredient[2]['amount'] = '300';
$xml->ingredient[2]['unit'] = 'грамм';
print_r($xml->ingredient[2]);
Результат:
SimpleXMLElement Object
(
[@attributes] => Array
(
[name] => хлеб
[preptime] => 5
[cooktime] => 180
)
[title] => Ржаной хлеб
[ingredient] => Array
(
[0] => Ржаная мука
[1] => Дрожжи
[2] => Тёплая вода
[3] => Соль
)
[instructions] => SimpleXMLElement Object
(
[step] => Array
(
[0] => Смешать все ингредиенты и тщательно замесить.
[1] => Закрыть тканью и оставить на один час в тёплом помещении.
[2] => Замесить ещё раз, положить на противень и поставить в духовку.
)
)
)
SimpleXMLElement Object
(
[@attributes] => Array
(
[amount] => 300
[unit] => грамм
)
[0] => Тёплая вода
)
Чтобы добавить дочерний элемент к текущему, достаточно использовать метод addChild(). Первым параметром идет имя нового элемента, вторым значение, которое задавать необязательно.
Добавим еще один шаг к инструкциям:
$node = $xml->instructions; // получаем ветвь инструкций
$node->addChild('step', 'Почитать газету'); // добавляем элемент
print_r($node);
Результат:
SimpleXMLElement Object
(
[step] => Array
(
[0] => Смешать все ингредиенты и тщательно замесить.
[1] => Закрыть тканью и оставить на один час в тёплом помещении.
[2] => Замесить ещё раз, положить на противень и поставить в духовку.
[3] => Почитать газету
)
)
Метод addAttribute()
позволяет добавить атрибут к текущему узлу. Первый параметр это имя атрибута, второй значение.
$node = $xml->instructions; // получаем ветвь инструкций
$step = $node->addChild('step', 'Почитать газету'); // добавляем элемент
$step->addAttribute('newspaper', 'Аргументы и факты'); // добавляем артибут
print_r($step);
Результат:
SimpleXMLElement Object
(
[@attributes] => Array
(
[newspaper] => Аргументы и факты
)
[0] => Почитать газету
)
SimpleXML включает в себя встроенную поддержку XPath. Поиск всех элементов <step>
:
foreach ($xml->xpath('//step') as $step) {
echo $step . '<br/>';
}
Результат:
Смешать все ингредиенты и тщательно замесить.<br/>
Закрыть тканью и оставить на один час в тёплом помещении.<br/>
Замесить ещё раз, положить на противень и поставить в духовку.<br/>
PHP может преобразовывать XML узлы из SimpleXML в формат DOM и наоборот. Этот пример показывает, как можно изменить DOM элемент в SimpleXML:
$dom = new DOMDocument('1.0', 'utf-8');
$dom->load('recipe.xml');
$xml = simplexml_import_dom($dom);