Содержание

Перевод: 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

Сайт библиотеки