Учимся парсить сайты с библиотекой PHP Simple HTML DOM Parser
Те, кто хоть раз писал парсер, знает, что не стоит этого делать с помощью регулярных выражений. Проиллюстрировать это утверждение поможет следующий пример.
Возьмем HTML код:
К примеру, из него нам нужно получить описание и url сайта. Если брать исключительно этот кусок кода, то все решается достаточно просто:
Проблемы начинаются тогда, когда описание сайта заполняют пользователи, и оно не имеет определенного шаблона.
Такой код регулярному выражению не по зубам.
Обычно, в вузах на этот случай учат писать конечный автомат. Суть его в том, что мы перебираем, посимвольно, весь html текст, находим начало тега, и строим дерево документа. Так называемое DOM (Document Object Model)
Сейчас, писать такое самому нет необходимости.
В php, начиная с версии 5, есть встроенные методы работы с деревом документа (класс DOMDocument), но основан он на XML парсере.
А HTML и XML это хоть и очень похожие, но в тоже время абсолютно разные технологии.
К примеру, непременное требование к XML это закрытые теги и отсутствие ошибок.
Отсюда вытекает условие: ошибок в html, который мы парсим с помощью нативных средств php, быть не должно.
К сожалению, на сайтах донорах, ошибки не редки, а значит этот метод отпадает.
Для корректного разбора таких сайтов, на помощь придут php библиотеки PHPQuery, Simple HTML DOM, Zend DOM Query, Nokogiri .
Некоторые из них, после небольших манипуляций скармливают html тому же DOMDocument. Мы не будем их рассматривать.
В этой статье я расскажу про SimpleHTMLDOM. Этой библиотекой я пользуюсь уже несколько лет, и она меня еще ни разу не подводила.
Скачиваем последнюю версию здесь.
Пусть Вас не смущает то, что она не обновлялась с 2008 года, то, что она умеет, полностью покроет Ваши нужды в разборе html текстов.
В архиве, который вы скачали, две папки (примеры работы и документация) и файл simple_html_dom.php.
simple_html_dom.php это и есть вся библиотека, больше ничего для работы не потребуется. Кидаем этот файл в папку с проектом и в своем скрипте просто подгружаем его.
Кроме документации, которую вы скачали с архивом, доступна еще online версия, ее вы найдете здесь
Файл подключен и готов к работе.
Для того, чтобы начать разбирать HTML, его сперва нужно получить. Обычно, я делаю это при помощи библиотеки CURL.
В simplehtmldom есть методы для удаленной загрузки страниц. После подключения файла библиотеки, нам доступны 2 функции для обработки HTML строк.
str_get_htm(str) и file_get_html(url)
Они делают одно и тоже, преобразуют HTML текст в DOM дерево, различаются лишь источники.
str_get_htm – на вход получает обычную строку, т.е. если вы получили HTML прибегнув к curl, или file_get_contents то вы просто передаете полученный текст этой функции.
file_get_html – сама умеет загружать данные с удаленного URL или из локального файла
или
К сожалению, file_get_html загружает страницы обычным file_get_contents. Это значит если хостер, выставил в php.ini allow_url_fopen = false (т.е. запретил удаленно открывать файлы), то загрузить что-то удаленно, не получится. Да и серьезные веб сайты таким способом парсить не стоит, лучше использовать CURL с поддержкой proxy и ssl. Однако для наших опытов, вполне хватит и file_get_html.
в результате, в переменной $html будет объект типа simple_html_dom.
При больших объемах данных, в библиотеке происходит утечка памяти. Поэтому после окончания одного цикла надо ее чистить.
Делает это метод clear.
К примеру грузим 5 раз сайт www.yandex.ru с разными поисковыми запросами
Эти две строчки $html->clear(); и unset($html); лучше писать сразу же после того, как Вы создали объект. Иначе забудете, и скрипт отвалится, забив всю память.
После того, как html текст упакован в объект, можно приступать непосредственно к поиску нужных элементов.
Большинство поисковых функций выполняет метод find(selector, [index]). Если второй аргумент не задан, метод возвращает массив элементов. Если же задан то элемент этого массива с индексом index.
Пример: скачаем главную страницу моего блога, и выведем все ссылки, которые встретим на своем пути.
В примере, в качестве селектора я воспользовался названием тега <a>. Но можно использовать и другие CSS селекторы. Элемент на странице можно найти по его атрибутам. В первую очередь, это название тега, id и class. Также могут быть использованы и второстепенные атрибуты, к примеру, href ссылки или width картинки. Если и этих атрибутов нет, то не грех воспользоваться и регулярными выражениями.
Поиск по названию тега вы уже видели
поиск по id
поиск по классу
или комбинированный вариант
в данном случае, сначала найдется элемент с id= preview затем в нем найдутся все теги div, и уже среди них фильтруются те у которых class=”myclass”
Если метод find ничего не нашел и index не задан, то он возвращает пустой массив. Если же index задан, то метод возвращает null.
Поэтому верным решением будет проверить
Поиск по наличию атрибута
или более конкретный поиск по значению атрибута
Такая нотация позволяет искать по двум и более смежным классам
Поиск нескольких тегов
Поиск вложенных тегов
У каждого найденного элемента также есть метод find
если нам нужно найти все li только первого div’а то мы можем написать так
Поиск по значению атрибута не ограничивается только равенством. Вот доступные условия
[атрибут] – проверяет есть ли у элемента данный атрибут
[атрибут=величина] - проверяет, есть ли у элемента данный атрибут и равно ли его значение величине.( div[class=myclass] – найдет все div’ы у которых class равен myclass)
[атрибут!=величина] - проверяет, есть ли у элемента данный атрибут и не равно ли его значение величине.( div[class!=myclassok] – найдет все div’ы у которых class не равен myclassok)
[атрибут^=величина] - проверяет, есть ли у элемента данный атрибут и начинается ли его значение с величины ( div[class^=my] – найдет все div’ы у которых class начинается с my, к примеру myclass и myclassok)
[атрибут$=величина] - проверяет, есть ли у элемента данный атрибут и заканчивается ли его значение величиной( div[class$=ok] – найдет все div’ы у которых class заканчивается на ok, к примеру myclassok, yok, okно не oki)
[атрибут*=величина] - проверяет, есть ли у элемента данный атрибут и содержит ли его значение в себе величину, в любом месте(div[class*=sok] – найдет все div’ы у которых class содержит sok, к примеру myclassok, ysoki, sok)
Обычный текст можно искать как тег text
Комментарии находим по тегу comment
Каждый найденный элемент и сам $html имеют 5 полей
$e->tag Читает или записывает имя тега элемента.
$e->outertext Читает или записывает весь HTML элемента, включая его самого.
$e->innertext Читает или записывает внутренний HTML элемента
$e->plaintext Читает или записывает простой текст элемента, это эквивалентно функции strip_tags($e->innertext). Хотя поле доступно для записи, запись в него ничего не даст, и исходный html не изменит
Как Вы могли догадаться, для удаления ненужного элемента из HTML можно затереть его поле outertext
Тут следует помнить, что хоть элемент и не виден в html, из дерева DOM он никуда не делся
при желании мы даже можем вернуть элемент на место
Для более эффективной навигации по дереву документа доступны методы
$e->children ( [int $index] ) Возвращает объект N-го прямого потомка, если индекс установлен, в противном случае возвращает массив всех дочерних элементов
$e->parent() Возвращает родительский элемент.
$e->first_child() Возвращает первый дочерний элемент, или null, если ничего не найдено
$e->last_child() Возвращает последний дочерний элемент, или null, если ничего не найдено
$e->next_sibling() Возвращает следующий родственный элемент, или null, если ничего не найдено
$e->prev_sibling() Возвращает предыдущий родственный элемент, или null, если ничего не найдено
пример
Все дочерние элементы разные, как-то подобрать к ним селектор проблематично. Поэтому воспользуемся описанными методами.
либо так
Данные методы полезны при разборе таблиц, элементы которых, как правило, структурированы, но не имеют идентифицирующих атрибутов.
Ну и последняя фишка это вызов callback функции на найденный элемент
На экране мы увидим
Доступ к атрибутам элементов осуществляется напрямую
Хватит теории, перейдем к практике
Загрузим n фотографий из поисковой выдачи Yandex Картинок. http://images.yandex.ru/
Как быть если нам нужно больше фото, чем лежит на одной странице?
Ответ прост: Код, приведенный выше, заключается в функцию, в html помимо фото находим еще и URLвсех страниц, и рекурсивно вызываем данную функцию для этих страниц.
Все хорошо, 200 картинок лежат в папке data. Но их размер слишком мал.
Поэтому завершающим аккордом нашей практики будет загрузка увеличенной фотографии.
Для этого определим еще одну функцию
и слегка поправим getYandexImages
Вот и все, наслаждаемся фото великолепной Джессики Альбы. Надеюсь меня простит Яндекс, ведь по сути фото грабится не с их серверов, а с прямиком с сайтов, где они лежат.
Кроме того это всего лишь демонстрация работы. Думаю никому в здравом уме, не придет в голову парсить Яндекс с помощью file_get_content. Данную библиотеку можно применять и в мирном программировании. К примеру в качестве шаблонизатора для CMS. Почему нет, с хорошим кешированием будет очень удобная штука.
Учимся парсить сайты с библиотекой PHP Simple HTML DOM Parser - Генератор расширений Joomla и многое другое на нашем сайте посвященном работе расширений, компонентов, модулей, плагинов для линейки Joomla. Отправляйте ссылку на страницу своим друзьям и в социальные сети воспользовавшись графическими иконками выше.