DiDOM - простая и быстрая библиотека для парсинга HTML.
Содержание
Перевод: https://github.com/Imangazaliev/DiDOM
- Установка
- Быстрый старт
- Создание нового документа
- Поиск элементов
- Проверка наличия элемента
- Поддерживамые селекторы
- Изменение содержимого
- Вывод содержимого
- Создание нового элемента
- Получение родительского элемента
- Получение соседних элементов
- Получение дочерних элементов
- Получение документа
- Работа с атрибутами элемента
- Сравнение элементов
- Добавление дочерних элементов
- Замена элемента
- Удаление элемента
- Работа с кэшем
- Прочее
- Сравнение с другими парсерами
Установка
Для установки DiDOM выполните команду:
composer require imangazaliev/didom
Быстрый старт
use DiDom\Document;
$document = new Document('http://www.news.com/', true);
$posts = $document->find('.post');
foreach($posts as $post) {
echo $post->text(), "\n";
}
Создание нового документа
DiDom позволяет загрузить HTML несколькими способами:
Через конструктор
// в первом параметре передается строка с HTML
$document = new Document($html);
// путь к файлу
$document = new Document('page.html', true);
// или URL
$document = new Document('http://www.example.com/', true);
Второй параметр указывает на то, что загружается файл. По умолчанию - false.
Через отдельные методы
$document = new Document();
$document->loadHtml($html);
$document->loadHtmlFile('page.html');
$document->loadHtmlFile('http://www.example.com/');
Для загрузки XML есть соответствующие методы loadXml и loadXmlFile.
При загрузке через эти методы документу можно передать дополнительные параметры:
$document->loadHtml($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
Поиск элементов
В качестве выражения для поиска можно передать CSS-селектор или XPath-путь. Для этого в первом параметре нужно передать само выражение, а во втором - его тип (по умолчанию - Query::TYPE_CSS):
Через метод find:
use DiDom\Document;
use DiDom\Query;
...
// CSS-селектор
$posts = $document->find('.post');
// XPath-выражение
$posts = $document->find("//div[contains(@class, 'post')]", Query::TYPE_XPATH);
Через метод first:
Возвращает первый найденный элемент, либо null, если элементы, подходящие под селектор не были найдены.
Через магический метод __invoke():
$posts = $document('.post');
Через метод xpath:
$posts = $document->xpath("//*[contains(concat(' ', normalize-space(@class), ' '), ' post ')]");
Можно осуществлять поиск и внутри элемента:
echo $document->find('.post')[0]->find('h2')[0]->text();
Если элементы, соответствующие заданному выражению, найдены, метод вернет массив с экземплярами класса DiDom\Element, иначе - пустой массив. При желании можно получить массив объектов DOMElement. Для этого необходимо передать в качестве третьего параметра false.
Проверка наличия элемента
Проверить наличие элемента можно с помощью метода has():
if ($document->has('.post')) {
// код
}
Если нужно проверить наличие элемента, а затем получить его, то можно сделать так:
if ($document->has('.post')) {
$elements = $document->find('.post');
// код
}
но быстрее так:
if (count($elements = $document->find('.post')) > 0) {
// код
}
т.к. в первом случае выполняется два запроса.
Поддерживамые селекторы
DiDom поддерживает поиск по:
- тэгу
- классу, идентификатору, имени и значению атрибута
- псевдоклассам:
- first-, last-, nth-child
- empty и not-empty
- contains
- has
// все ссылки
$document->find('a');
// любой элемент с id = "foo" и классом "bar"
$document->find('#foo.bar');
// любой элемент, у которого есть атрибут "name"
$document->find('[name]');
// эквивалентно
$document->find('*[name]');
// поле ввода с именем "foo"
$document->find('input[name=foo]');
$document->find('input[name=\'foo\']');
$document->find('input[name="foo"]');
// поле ввода, название которого НЕ равно "foo"
$document->find('input[name!="foo"]');
// любой элемент, у которого есть атрибут,
// начинающийся с "data-" и равный "foo"
$document->find('*[^data-=foo]');
// все ссылки, у которых адрес начинается с https
$document->find('a[href^=https]');
// все изображения с расширением png
$document->find('img[src$=png]');
// все ссылки, содержащие в своем адресе строку "example.com"
$document->find('a[href*=example.com]');
// все ссылки, содержащие в атрибуте data-foo значение bar отделенное пробелом
$document->find('a[data-foo~=bar]');
// текст всех ссылок с классом "foo"
$document->find('a.foo::text');
// адрес и текст подсказки всех полей с классом "bar"
$document->find('a.bar::attr(href|title)');
// все ссылки, которые являются прямыми потомками текущего элемента
$element->find('> a');
Изменение содержимого
Изменение HTML
$element->setInnerHtml('<a href="#">Foo</a>');
Изменение значения
$element->setValue('Foo');
Вывод содержимого
Получение HTML
Через метод html():
$posts = $document->find('.post');
echo $posts[0]->html();
Приведение к строке:
$html = (string) $posts[0];
Форматирование HTML при выводе
$html = $document->format()->html();
Метод format() отсутствует у элемента, поэтому, если нужно получить отформатированный HTML-код элемента, необходимо сначала преобразовать его в документ:
$html = $element->toDocument()->format()->html();
Внутренний HTML
$innerHtml = $element->innerHtml();
Метод innerHtml() отсутствует у документа, поэтому, если нужно получить внутренний HTML-код документа, необходимо сначала преобразовать его в элемент:
$innerHtml = $document->toElement()->innerHtml();
Дополнительные параметры
$html = $document->format()->html(LIBXML_NOEMPTYTAG);
Получение содержимого
echo $element->text();
Создание нового элемента
Создание экземпляра класса
use DiDom\Element;
$element = new Element('span', 'Hello');
// выведет "<span>Hello</span>"
echo $element->html();
Первым параметром передается название элемента, вторым - его значение (необязательно), третьим - атрибуты элемента (необязательно).
Пример создания элемента с атрибутами:
$attributes = ['name' => 'description', 'placeholder' => 'Enter description of item'];
$element = new Element('textarea', 'Text', $attributes);
Элемент можно создать и из экземпляра класса DOMElement:
use DiDom\Element;
use DOMElement;
$domElement = new DOMElement('span', 'Hello');
$element = new Element($domElement);
С помощью метода createElement
$document = new Document($html);
$element = $document->createElement('span', 'Hello');
С помощью CSS-селектора
$document = new Document($html);
$element = $document->createElementBySelector('div.block', 'Foo', ['id' => '#content']);
Можно так же использовать статический метод createBySelector класса Element:
$element = Element::createBySelector('div.block', 'Foo', ['id' => '#content']);
Получение родительского элемента
$element->parent();
Так же можно получить родительский элемент, соответствующий селектору:
$element->closest('.foo');
Вернет родительский элемент, у которого есть класс foo. Если подходящий элемент не найден, метод вернет null.
Получение соседних элементов
$document = new Document($html);
$item = $document->find('ul.menu > li')[1];
// предыдущий элемент
var_dump($item->previousSibling());
// следующий элемент
var_dump($item->nextSibling());
Получение дочерних элементов
$html = '
<ul>
<li>Foo</li>
<li>Bar</li>
<li>Baz</li>
</ul>
';
$document = new Document($html);
$list = $document->first('ul');
// string(3) "Baz"
var_dump($item->child(2)->text());
// string(3) "Foo"
var_dump($item->firstChild()->text());
// string(3) "Baz"
var_dump($item->lastChild()->text());
// array(3) { ... }
var_dump($item->children());
Получение документа
$document = new Document($html);
$element = $document->first('input[name=email]');
$document2 = $element->getDocument();
// bool(true)
var_dump($document->is($document2));
Работа с атрибутами элемента
Получение названия элемента
$name = $element->tag;
Создание/изменение атрибута
Через метод setAttribute:
$element->setAttribute('name', 'username');
Через метод attr:
$element->attr('name', 'username');
Через магический метод __set:
$element->name = 'username';
Получение значения атрибута
Через метод getAttribute:
$username = $element->getAttribute('value');
Через метод attr:
$username = $element->attr('value');
Через магический метод __get:
$username = $element->name;
Если атрибут не найден, вернет null.
Проверка наличия атрибута
Через метод hasAttribute:
if ($element->hasAttribute('name')) {
// код
}
Через магический метод __isset:
if (isset($element->name)) {
// код
}
Удаление атрибута:
Через метод removeAttribute:
$element->removeAttribute('name');
Через магический метод __unset:
unset($element->name);
Получение всех атрибутов:
var_dump($element->attributes());
Сравнение элементов
$element = new Element('span', 'hello');
$element2 = new Element('span', 'hello');
// bool(true)
var_dump($element->is($element));
// bool(false)
var_dump($element->is($element2));
Добавление дочерних элементов
$list = new Element('ul');
$item = new Element('li', 'Item 1');
$list->appendChild($item);
$items = [
new Element('li', 'Item 2'),
new Element('li', 'Item 3'),
];
$list->appendChild($items);
Замена элемента
$title = new Element('title', 'foo');
$document->first('title')->replace($title);
Внимание: заменить можно только те элементы, которые были найдены непосредственно в документе:
// ничего не выйдет
$document->first('head')->first('title')->replace($title);
// а вот так да
$document->first('head title')->replace($title);
Удаление элемента
$document->first('title')->remove();
Внимание: удалить можно только те элементы, которые были найдены непосредственно в документе:
// ничего не выйдет
$document->first('head')->first('title')->remove();
// а вот так да
$document->first('head title')->remove();
Работа с кэшем
Кэш - массив XPath-выражений, полученных из CSS.
Получение кэша
use DiDom\Query;
...
$xpath = Query::compile('h2');
$compiled = Query::getCompiled();
// array('h2' => '//h2')
var_dump($compiled);
Установка кэша
Query::setCompiled(['h2' => '//h2']);
Прочее
preserveWhiteSpace
По умолчанию сохранение пробелов между тегами отключено.
Включать опцию preserveWhiteSpace следует до загрузки документа:
$document = new Document();
$document->preserveWhiteSpace();
$document->loadXml($xml);
count
Метод count() позволяется подсчитать количество дочерних элементов, соотвествующих селектору;
// выведет количество ссылок в документе
echo $document->count('a');
matches
Возвращает true, если узел подходит под селектор:
$element->matches('div#content');
isTextNode
Проверяет, является ли элемент текстовым узлом (DOMText):
$element->isTextNode();
isCommentNode
Проверяет, является ли элемент комментарием (DOMComment):
$element->isCommentNode();
Сравнение с другими парсерами
Файлы библиотеки. DiDOM-master.zip