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