Php simple html dom parser инструкция

copysite

Те, кто хоть раз писал парсер, знает, что не стоит этого делать с помощью регулярных выражений. Проиллюстрировать это утверждение поможет следующий пример.

 Возьмем HTML код: 

<div><a href="http://xdan.ru"><div>Сайт по программированию парсеров</div><div> и многое другое</div></a></div>

К примеру, из него нам нужно получить описание и url сайта. Если брать исключительно этот кусок кода, то все решается достаточно просто: 

$html = '<div><a href="http://xdan.ru"><div>Сайт по программированию парсеров</div><div> и многое другое</div></a></div>';
preg_match('#<div><a href="([^"]+)"><div>([^<]+)</div><div>([^<]+)</div></a></div>#U',$html,$list);
echo 'url:'.$list[1].',title:'.$list[2].$list[3]; // выведет url:http://xdan.ru,title:Сайт по программированию парсеров и многое другое

Проблемы начинаются тогда, когда описание сайта заполняют пользователи, и оно не имеет   определенного шаблона.

<div><a href=”http://xdan.ru”><div>Сайт по <b>программированию</b> парсеров</div><div> и многое <div> многое </div> другое </div></a></div>

Такой код регулярному выражению не по зубам.

Обычно, в вузах на этот случай учат писать конечный автомат. Суть его в том, что мы перебираем, посимвольно, весь 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 это и есть вся библиотека, больше ничего для работы не потребуется. Кидаем этот файл в папку с проектом и в своем скрипте просто подгружаем его.

include 'simple_html_dom.php';

Кроме документации, которую вы скачали с архивом, доступна еще online версия, ее  вы найдете здесь

Файл подключен и готов к работе.

Для того, чтобы начать разбирать HTML, его сперва нужно получить. Обычно, я делаю это при помощи библиотеки CURL.

В simplehtmldom есть методы для удаленной загрузки страниц.  После подключения файла библиотеки, нам доступны 2 функции для обработки HTML строк.

str_get_html(str) и file_get_html(url)

Они  делают одно и тоже, преобразуют HTML текст в DOM дерево, различаются лишь источники.

str_get_htm – на вход получает обычную строку, т.е. если вы получили HTML прибегнув к curl, или file_get_contents то вы просто передаете полученный текст этой функции.

$html = str_get_html('<html><body>Привет!</body></html>');

file_get_html – сама умеет загружать данные с удаленного URL или из локального файла

$html = file_get_html('http://www.yandex.ru/');

или

$html = file_get_html('data/test.htm');

К сожалению,  file_get_html загружает страницы обычным file_get_contents. Это значит если хостер, выставил в php.ini allow_url_fopen = false (т.е. запретил удаленно открывать файлы), то загрузить что-то удаленно, не получится. Да и серьезные веб сайты таким способом парсить не стоит, лучше использовать CURL с поддержкой proxy и ssl. Однако для наших опытов, вполне хватит и file_get_html.

$html = file_get_html('http://www.yandex.ru/'); 

в результате, в  переменной $html будет объект типа simple_html_dom.

При больших объемах данных, в библиотеке происходит утечка памяти. Поэтому после окончания одного цикла надо ее чистить.

Делает это метод clear.

К примеру грузим 5 раз сайт www.yandex.ru с разными поисковыми запросами

$k = 5; 
while($k>0){
 $html = file_get_html('http://yandex.ru/yandsearch?text=hi'.$k.'&lr=11114'); // загружаем данные
 // как-то их обрабатываем
 $html->clear(); // подчищаем за собой
 unset($html);
 $k--;
}

Эти две строчки $html->clear();  и unset($html);  лучше писать сразу же после того, как  Вы создали объект. Иначе забудете, и скрипт отвалится, забив всю память.

После того, как html текст упакован в объект, можно приступать непосредственно к поиску нужных элементов. 

Большинство поисковых функций выполняет метод find(selector, [index]).  Если второй аргумент не задан, метод возвращает массив элементов. Если же задан то элемент этого массива с индексом index.

Пример: скачаем главную страницу моего блога, и выведем все ссылки, которые встретим на своем пути.

require_once 'simple_html_dom.php';
$data = file_get_html('http://xdan.ru');
if($data->innertext!='' and count($data->find('a'))){
	foreach($data->find('a') as $a){
		echo '<a href="http://xdan.ru/'.$a->href.'">'.$a->plaintext.'</a></br>';
	}
}

В примере, в качестве селектора я воспользовался названием тега <a>. Но можно использовать и другие CSS селекторы. Элемент на странице можно найти по его атрибутам. В первую очередь, это название тега, id и class. Также могут быть использованы и второстепенные атрибуты, к примеру, href ссылки или width картинки. Если и этих атрибутов нет, то не грех воспользоваться и регулярными выражениями.

Поиск по названию тега вы уже видели

$html->find('div')

поиск по id

$html->find('#preview')

поиск по классу

$html->find('.myclass')

или комбинированный вариант

$html->find('#preview div.myclass')

в данном случае, сначала найдется элемент с id= preview затем в нем найдутся все теги div, и уже среди них фильтруются те у которых class=”myclass”

Если метод find ничего не нашел и index не задан, то он возвращает пустой массив. Если же index задан, то метод возвращает null.  

Поэтому верным решением будет проверить

if(count($html->find('#preview div.myclass')))
	foreach($html->find('#preview div.myclass') as $div)
		echo $div->innertext;

Поиск по наличию атрибута

$html->find(' img [width]'); // найдет нам все изображения у которых задан атрибут ширина

или более конкретный поиск по значению атрибута

$ret = $html->find('img[width=400px]');// найдет все изображения, у которых задана ширина равная 400px

Такая нотация позволяет искать по двум и более смежным классам

$ret = $html->find('img[class=active myclass]');//<img class="active myclass"/>

Поиск нескольких тегов

$html->find('a, img, br,span');

Поиск вложенных тегов

$es = $html->find('ul.myclass li');// найдет все li который является потомком ul(возможно и не прямым)
$es = $html->find('div.myclass li');// найдет все li в  div.myclass

У каждого найденного элемента также есть метод find

$html->find('div.myclass li');//найдет все div.myclass а потом все li лежащие в них

если нам нужно найти все li только первого div’а то мы можем написать так

$html->find('div.myclass',0)->find('li');

Поиск по значению атрибута не ограничивается только равенством. Вот доступные условия

[атрибут] – проверяет есть ли у элемента данный атрибут

[атрибут=величина] — проверяет, есть ли у элемента данный атрибут и равно ли его значение величине.( 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

$es = $html->find('text'); // найдет все текстовые блоки в html

Комментарии находим по тегу comment

$es = $html->find('comment');

Каждый найденный элемент и сам $html имеют 5 полей

$html = str_get_html("<div>foo <b>bar</b></div>"); 
echo $html; // выведет <div>foo <b>bar</b></div>;
$e = $html->find("div", 0);
echo $e->tag; // Вернет: "div"
echo $e->outertext; // Вернет: <div>foo <b>bar</b></div>
echo $e->innertext; // Вернет: foo <b>bar</b>
echo $e->plaintext; // Вернет: foo bar

$e->tag            Читает или записывает имя тега элемента.

$e->outertext   Читает или записывает весь HTML элемента, включая его самого.

$e->innertext   Читает или записывает внутренний HTML элемента

$e->plaintext    Читает или записывает простой текст элемента, это эквивалентно функции strip_tags($e->innertext). Хотя поле доступно для записи, запись в него ничего не даст, и исходный html не изменит

$html = str_get_html("<div>foo <b>bar</b></div"); 
$div = $html->find('div',0);
$div->plaintext = 'gooo';
echo $div->innertext; // вернет <div>foo <b>bar</b></div>

Как  Вы могли догадаться, для удаления ненужного элемента из HTML можно затереть его поле  outertext

$html = str_get_html("<div>foo <b>bar</b></div"); 
$b = $html->find('b',0);
$b->outertext = '';
echo $html->innertext; // вернет <div>foo</div>

Тут следует помнить, что хоть элемент и не виден в html, из дерева DOM он никуда не делся

$html = str_get_html("<div>foo <b>bar</b></div"); 
$b = $html->find('b',0);
$b->outertext = '';
echo $html->innertext; // вернет <div>foo</div>, элемент удален из HTML
// но
echo count($html->find('b')); // вернет 1, в дерево элемент присутствует

при желании  мы даже можем вернуть элемент на место

$b->outertext = '<span>bar</span>';
echo $html->innertext;// вернет <div>foo<span>bar</span></div>

Для более эффективной навигации по дереву документа доступны методы

$e->children ( [int $index] )     Возвращает объект N-го прямого потомка, если индекс установлен, в противном случае возвращает массив всех дочерних элементов

$e->parent()    Возвращает родительский элемент.

$e->first_child() Возвращает первый дочерний элемент, или null, если ничего не найдено

$e->last_child() Возвращает последний дочерний элемент, или null, если ничего не найдено

$e->next_sibling() Возвращает следующий родственный элемент, или null, если ничего не найдено

$e->prev_sibling()       Возвращает предыдущий родственный элемент, или null, если ничего не найдено

пример

$html ="<div>
  <b>bar</b>
  <b>foo</b>
  <span>arg</span>
  <div>
    <b>tor</b>
  </div>
</div>"; 

Все дочерние элементы разные, как-то подобрать к ним селектор проблематично. Поэтому воспользуемся описанными методами.

$html  = str_get_html($html);
$div = $html->find('div',0);
$i = 0;
while($item = $div->children($i++)){
	echo $item->innertext;
} 

либо так

$item = $div->children(0);
echo $item->innertext;
while($item = $item -> next_sibling()){
   echo $item->innertext;
} 

Данные методы полезны при разборе таблиц, элементы которых, как правило, структурированы, но не имеют идентифицирующих атрибутов.

Ну и последняя фишка это вызов callback функции на найденный элемент

function my_callback($element) {
       if ($element->tag=='span')
                $element->outertext = '<b>'.$element->innertext. '</b>';// заменим все span элементы на b
} 
$html  = str_get_html('<span>bar</span><span>pole</span><span>sushi</span><a>okno</a>');
// Регистрация функции обратного вызова с ее именем
$html->set_callback('my_callback');// вызов функции произойдет при конвертации объекта в строку
echo $html; // на самом деле, при этом вызывается магический метод __toString, он и запускает наши калбяки

На экране мы увидим 

<b>bar</b><b>pole</b><b>sushi</b><a>okno</a>

Доступ к атрибутам элементов осуществляется напрямую

foreach($html->find('img') as $img) echo $img->src;
//или
echo $html->find('img',0)->src;

Хватит теории, перейдем к практике

Загрузим n фотографий из поисковой выдачи Yandex Картинок. http://images.yandex.ru/

require_once 'simple_html_dom.php';
// поисковый URL
$url = 'http://images.yandex.ru/yandsearch?text='.urlencode('Джессика Альба').'&rpt=image';
$n = 2;
// загружаем данный URL
$data = file_get_html($url);
// очищаем страницу от лишних данных, это не обязательно, но когда HTML сильно захламлен бывает удобно почистить его, для дальнейшего анализа
foreach($data->find('script,link,comment') as $tmp)$tmp->outertext = '';
// находим все изображения на странице
if(count($data->find('div.b-image img'))){
	$i = 1;
	foreach($data->find('div.b-image img') as $img){
		// выводим на экран изображение 
		echo '<img src="'.$img->src.'"/>';
		// и скачиваем его в файл
		file_put_contents('data/'.($i++).'.jpg',file_get_contents($img->src));
		if($i>$n)break; // выходим из цикла если скачали достаточно фотографий
	}
}
$data->clear();// подчищаем за собой
unset($data);

Как быть если нам нужно больше фото, чем лежит на одной странице?

Ответ прост: Код, приведенный выше, заключается в функцию, в html помимо фото находим еще  и URLвсех страниц, и рекурсивно вызываем данную функцию для этих страниц.

require_once 'simple_html_dom.php';
function getYandexImages($url,$findpages = true){
	static $i = 1;
	$n = 200;
	// загружаем данный URL
	$data = file_get_html($url);
	// очищаем страницу от лишних данных, это не обязательно, но когда HTML сильно захламлен бывает удобно почистить его, для дальнейшего анализа
	foreach($data->find('script,link,comment') as $tmp)$tmp->outertext = '';
	// находим URL страниц только для первого вызова функции
	if( $findpages and count($data->find('div.b-pager__pages a'))){
		foreach($data->find('div.b-pager__pages a') as $a){	
			// довольно распространенный случай - локальный URL. Поэтому иногда url надо дополнять до полного
			if( !preg_match('#^http://#',$a->href) )$a->href = 'http://images.yandex.ru'.$a->href;
			// и еще дна тонкость, & надо заменять на &
			$a->href = str_replace('&','&',$a->href);
			// вызываем функцию для каждой страницы
			getYandexImages($a->href,false);
		}
	}
	// находим все изображения на странице
	if(count($data->find('div.b-image img'))){
		foreach($data->find('div.b-image img') as $img){
			// выводим на экран изображение 
			echo '<img src="'.$img->src.'"/>';
			// и скачиваем его в файл
			file_put_contents('data/'.($i++).'.jpg',file_get_contents($img->src));
			if($i>$n)exit; // завершаем работу если скачали достаточно фотографий
		}
	}
	$data->clear();// подчищаем за собой
	unset($data);
}
// поисковый URL
$url = 'http://images.yandex.ru/yandsearch?text='.urlencode('Джессика Альба').'&rpt=image';
getYandexImages($url);

Все хорошо, 200 картинок лежат в папке data. Но их размер слишком мал.

Поэтому завершающим аккордом нашей практики будет загрузка увеличенной фотографии.

Для этого определим еще одну функцию

function getBigImage($url){
	$data = @file_get_contents($url);
	if(trim($data)=='')return false; // бывает что сайт недоступен, его фото мы не грузим
	$data = str_get_html($data);
	// находим фото
	if( count($data->find('#i-main-pic')) ){
		$dataimg = @file_get_contents($data->find('#i-main-pic',0)->src); // собачка нужна в если сервер нам вернул 404, это выозвет Warning:, поэтому экранируем ошибки
		if(trim($dataimg)=='')return false; // фото не доступно, его не грузим
		file_put_contents( 'data/'.md5($url).'.jpg', $dataimg ); // сохрпаняем в файл
	}
	$data->clear();// подчищаем за собой
	unset($data);
}

и слегка поправим getYandexImages

function getYandexImages($url,$findpages = true){
	global $i,$n;
	// загружаем данный URL
	$data = @file_get_contents($url);
	$data = str_get_html($data);
	// очищаем страницу от лишних данных, это не обязательно, но когда HTML сильно захламлен бывает удобно почистить его, для дальнейшего анализа
	foreach($data->find('script,link,comment') as $tmp)$tmp->outertext = '';
	// находим URL страниц только для первого вызова функции
	if( $findpages and count($data->find('div.b-pager__pages a'))){
		foreach($data->find('div.b-pager__pages a') as $a){	
			// довольно распространенный случай - локальный URL. Поэтому иногда url надо дополнять до полного
			if( !preg_match('#^http://#',$a->href) )$a->href = 'http://images.yandex.ru'.$a->href;
			// и еще дна тонкость, & надо заменять на &
			$a->href = str_replace('&','&',$a->href);
			// вызываем функцию для каждой страницы
			getYandexImages($a->href,false);
		}
	}
	// находим все изображения на странице
	if(count($data->find('div.b-image img'))){
		foreach($data->find('div.b-image a') as $a){
			if( !preg_match('#^http://#',$a->href) )$a->href = 'http://images.yandex.ru'.$a->href;
			$a->href = str_replace('&','&',$a->href);
			getBigImage($a->href);
			if($i++>=$n)exit; // завершаем работу если скачали достаточно фотографий
			echo '<script>document.getElementById("counter").innerHTML = "Загружено: '.$i.' из '.$n.' фото";</script>';
			flush();
		}
	}
	$data->clear();// подчищаем за собой
	unset($data);
}
// поисковый URL
$i = 1;
$n = 20; // будем грабить 20 картинок
$url = 'http://images.yandex.ru/yandsearch?text='.urlencode('Джессика Альба').'&rpt=image';
getYandexImages($url);

Вот и все, наслаждаемся фото великолепной Джессики Альбы. Надеюсь меня простит Яндекс, ведь по сути фото грабится не с их серверов, а с прямиком с сайтов, где они лежат.

Кроме того это всего лишь демонстрация работы. Думаю никому в здравом уме, не придет в голову парсить Яндекс с помощью file_get_content. Данную библиотеку можно применять и в мирном программировании. К примеру в качестве шаблонизатора для CMS. Почему нет, с хорошим кешированием будет очень удобная штука.

 При больших объемах сайтов доноров,  неплохо бы разбить все на потоки.

А используя описанный мой скрипт сортировки изображений по цвету, можно собрать неплохую отсортированную базу, фотографий знаменитостей.

И, как всегда, выкладываю все исходники

UPD: Благодаря пользователю Диме, выяснилось, что библиотека обновилась 2011-07-14 и имеет версию 1.5. Нововведений не много, однако самый главный баг, с которым сталкивались все, кто пользовался библиотекой, а именно утечка памяти, устранен. На то она и версия 1.5, а не 2.0. Радует, что проект все же поддерживается и развивается. Ждем новых релизов.

PHP Simple HTML DOM Parser

BASIC TESTS

simplehtmldom is a fast and reliable HTML DOM parser for PHP.

Key features

  • Purely PHP-based DOM parser (no XML extensions required).
  • Works with well-formed and broken HTML documents.
  • Loads webpages, local files and document strings.
  • Supports CSS selectors.

Requirements

simplehtmldom requires PHP 5.6 or higher with ext-iconv enabled. Following extensions enable additional features of the parser:

  • ext-mbstring (recommended)
    Enables better detection for multi-byte documents.
  • ext-curl
    Enables cURL support for the class HtmlWeb.
  • ext-openssl (recommended when using cURL)
    Enables SSL support for cURL.

Installation

Manually:

Download the latest release from SourceForge and extract the files in the vendor folder of your project.

Composer:

composer require simplehtmldom/simplehtmldom

Git:

git clone git://git.code.sf.net/p/simplehtmldom/repository simplehtmldom

Note: The GitHub repository serves as a mirror for the SourceForge project. We currently accept pull requests and issues only via SourceForge.

Usage

This example illustrates how to return the page title:

Manually

<?php
include_once 'HtmlWeb.php';
use simplehtmldom\HtmlWeb;

$client = new HtmlWeb();
$html = $client->load('https://www.google.com/search?q=simplehtmldom');

// Returns the page title
echo $html->find('title', 0)->plaintext . PHP_EOL;

Using composer

<?php
include_once 'vendor/autoload.php';
use simplehtmldom\HtmlWeb;

$client = new HtmlWeb();
$html = $client->load('https://www.google.com/search?q=simplehtmldom');

// Returns the page title
echo $html->find('title', 0)->plaintext . PHP_EOL;

Find more examples in the installation folder under examples.

Documentation

The documentation for this library is hosted at https://simplehtmldom.sourceforge.io/docs/

Getting involved

There are various ways for you to get involved with simplehtmldom. Here are a few:

  • Share this project with your friends (Twitter, Facebook, …you name it…).
  • Report bugs (SourceForge).
  • Request features (SourceForge).
  • Discuss existing bugs, features and ideas.

If you want to contribute code to the project, please open a feature request and include your patch with the message.

Authors

  • S.C. Chen
  • John Schlick
  • logmanoriginal
  • Rus Carroll
  • Yousuke Kumakura
  • Vadim Voituk

License

The source code for simplehtmldom is licensed under the MIT license. For further information read the LICENSE file in the root directory (should be located next to this README file).

Technical notes

simplehtmldom is a purely PHP-based DOM parser that doesn’t rely on external libraries like libxml, SimpleXML or PHP DOM. Doing so provides better control over the parsing algorithm and a much simpler API that even novice users can learn to use in a short amount of time.

Для получения дерева категорий с использованием PHP Simple HTML DOM Parser необходимо выполнить несколько шагов. Этот процесс включает парсинг HTML-кода, поиск элементов, представляющих категории, и построение иерархической структуры (дерева). Рассмотрим это подробно.

Как получить дерево категорий с помощью PHP Simple HTML DOM Parser

1. Установка PHP Simple HTML DOM Parser

Перед началом работы убедитесь, что вы подключили библиотеку. Вы можете скачать её здесь или установить через Composer:

composer require simplehtmldom/simplehtmldom

Если вы используете Composer, подключите автозагрузчик:

require 'vendor/autoload.php';
use simplehtmldom\HtmlWeb;

2. Анализ структуры HTML

Для успешного парсинга важно понять, как организованы категории в HTML. Обычно категории представлены в виде вложенных списков (<ul> или <ol>) или других элементов с определёнными классами/идентификаторами. Например:

<ul class="categories">
    <li>
        <a href="/category1">Category 1</a>
        <ul>
            <li><a href="/category1/sub1">Subcategory 1.1</a></li>
            <li><a href="/category1/sub2">Subcategory 1.2</a></li>
        </ul>
    </li>
    <li>
        <a href="/category2">Category 2</a>
    </li>
</ul>

Здесь видно, что категории организованы в виде вложенных списков, где каждый <li> содержит ссылку на категорию и, возможно, дочерние элементы.

3. Парсинг HTML и построение дерева категорий

Напишем функцию для рекурсивного обхода вложенных списков и создания дерева категорий.

<?php
require 'vendor/autoload.php';
use simplehtmldom\HtmlWeb;

function parseCategories($element) {
    $categories = [];

    // Находим только непосредственные дочерние элементы <li>
    foreach ($element->children() as $child) {
        if ($child->tag === 'li') { // Проверяем, что это элемент <li>
            // Находим ссылку внутри <li>
            $link = $child->find('a', 0);
            if ($link) {
                $category = [
                    'name' => $link->plaintext,
                    'url'  => $link->href,
                    'children' => []
                ];

                // Проверяем, есть ли вложенные категории (непосредственный дочерний <ul>)
                $nestedUl = $child->find('ul', 0);
                if ($nestedUl) {
                    $category['children'] = parseCategories($nestedUl); // Рекурсивный вызов
                }

                $categories[] = $category;
            }
        }
    }

    return $categories;
}

// Загружаем HTML-страницу
$html = new HtmlWeb();
$dom = $html->load('https://example.com');

// Находим корневой элемент списка категорий
$root = $dom->find('.categories', 0);

if ($root) {
    $tree = parseCategories($root);
    print_r($tree);
} else {
    echo "Категории не найдены.";
}
?>

4. Объяснение кода

  1. Функция parseCategories:
    • Принимает элемент DOM, который содержит список категорий.
    • Для каждого элемента <li> извлекает текст и URL ссылки.
    • Если внутри <li> есть вложенный <ul>, вызывает себя рекурсивно для обработки дочерних категорий.
  2. Рекурсия:
    • Рекурсивный подход позволяет обрабатывать любую глубину вложенности категорий.
  3. Загрузка HTML:
    • Используется метод load для загрузки HTML-страницы.
    • Поиск корневого элемента выполняется с помощью CSS-селектора (например, .categories).
  4. Вывод результата:
    • Дерево категорий выводится в виде массива, где каждая категория содержит название, URL и массив дочерних категорий.

5. Пример вывода

Для приведённого выше HTML результат будет выглядеть так:

Array
(
    [0] => Array
        (
            [name] => Category 1
            [url] => /category1
            [children] => Array
                (
                    [0] => Array
                        (
                            [name] => Subcategory 1.1
                            [url] => /category1/sub1
                            [children] => Array()
                        )
                    [1] => Array
                        (
                            [name] => Subcategory 1.2
                            [url] => /category1/sub2
                            [children] => Array()
                        )
                )
        )
    [1] => Array
        (
            [name] => Category 2
            [url] => /category2
            [children] => Array()
        )
)

6. Важные замечания

  • Ошибки парсинга: Убедитесь, что HTML-код валиден. Некорректная разметка может привести к ошибкам.
  • Производительность: Simple HTML DOM Parser может быть медленным для больших документов. В таких случаях рассмотрите использование более производительных инструментов, таких как DOMDocument или Symfony Crawler.
  • CSS-селекторы: Убедитесь, что используемые селекторы соответствуют структуре целевой страницы.

Ошибки при установке пакета

In PackageDiscoveryTrait.php line 339:

Could not find a version of package simplehtmldom/simplehtmldom matching your minimum-stability (stable). Require it with an explicit version constraint allowing its desired stability.

Ошибка, которую вы видите, связана с настройками minimum-stability в вашем composer.json. По умолчанию Composer использует уровень стабильности stable, что означает, что он будет искать только стабильные версии пакетов. Однако пакет simplehtmldom/simplehtmldom может не иметь стабильной версии (например, только dev-версии или beta).

1. Явное указание версии пакета

Если вы знаете конкретную версию пакета, которую хотите установить, укажите её явно. Например:

composer require simplehtmldom/simplehtmldom:^1.9

Здесь ^1.9 означает, что вы хотите установить самую последнюю версию из ветки 1.9.x.

Если вы не уверены, какая версия доступна, проверьте её на Packagist.

2. Изменение уровня стабильности

Если вы хотите разрешить установку пакетов с более низким уровнем стабильности (например, dev-версий), добавьте параметр --dev или измените настройки в composer.json.

Вариант 1: Установка с явным разрешением нестабильных версий

Вы можете временно разрешить установку нестабильных версий, добавив флаг @dev:

composer require simplehtmldom/simplehtmldom:@dev

Вариант 2: Изменение minimum-stability в composer.json

Откройте файл composer.json вашего проекта и добавьте или измените секцию minimum-stability:

{
    "minimum-stability": "dev",
    "prefer-stable": true
}
  • minimum-stability: "dev" позволяет устанавливать пакеты с любым уровнем стабильности.
  • prefer-stable: true указывает Composer использовать стабильные версии, если они доступны.

После этого выполните команду снова:

composer require simplehtmldom/simplehtmldom

3. Проверка доступных версий

Если вы не уверены, какие версии пакета доступны, выполните следующую команду:

composer show simplehtmldom/simplehtmldom --all

Это покажет все доступные версии пакета, включая их стабильность (например, dev-master, beta, stable).

4. Альтернативный подход: Установка без Composer

Если вы не хотите использовать Composer, вы можете скачать библиотеку напрямую с официального сайта или из репозитория GitHub. Затем подключите её вручную:

require 'path/to/simple_html_dom.php';

Однако использование Composer предпочтительнее, так как оно обеспечивает удобное управление зависимостями.

Итог

Наиболее простым решением является явное указание версии пакета:

composer require simplehtmldom/simplehtmldom:^1.9

Если это не помогает, измените уровень стабильности в composer.json или используйте флаг @dev.

Last Updated :
02 May, 2023

Web Scraping is a technique used to extract large amounts of data from websites extracted and saved them to a local file in your computer or to a database or can be used as API. Data displayed by most websites can be viewed by using a web browser only. They do not offer the functionality to save a copy of this data for use. Thus the only option is to copy and paste the selected data that is required, which in reality, is a very tedious job and may take hours complete. In other terms, Web Scraping is the technique of automating such a process, in place of manual work, the Web Scraping software performs the same task within seconds. 

Web scraping can be done by targeting the selected DOM components and then processing or storing the text between that DOM element of a web page. To do the same in PHP, there is an API that parses the whole page and looks for the required elements within the DOM. It is the Simple HTML DOM Parser. To know more about Web Scraping visit this article. One can download it by clicking this link. 

Example 1: The below-given example shows the use of this API, to display a Google search on the localhost.

HTML Code: 

html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content=

        "width=device-width, initial-scale=1.0">

    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <title>Document</title>

</head>

<body>

    <form action="GoogleSearch.php" method="POST">

        <input type="text" name="search">

        <br><br>

        <button>

            Search

        </button>

    </form>

</body>

</html>

PHP code: 

php

PHP code: This code will work if you have already searched for anything on the Google Search engine. 

php

<?php

include('simple_html_dom.php');

$html = file_get_html(

foreach($html->find('div.kCrYT') as $elements) {

    echo $elements->plaintext;

    break;

}

?>

Output:

GeeksforGeeks is a very fast-growing community among programmers
and have a reach of around 10 million+ readers globally. Writing will
surely enhance your knowledge of the subject as before writing any
 topic, you need to be very crisp and clear about it.

Время на прочтение3 мин

Количество просмотров146K

Добрый день, уважаемые хабровчане. В данном посте речь пойдет о совместном проекте S. C. Chen и John Schlick под названием PHP Simple HTML DOM Parser (ссылки на sourceforge).

Идея проекта — создать инструмент позволяющий работать с html кодом используя jQuery подобные селекторы. Оригинальная идея принадлежит Jose Solorzano’s и реализована для php четвертой версии. Данный же проект является более усовершенствованной версией базирующейся на php5+.

В обзоре будут представлены краткие выдержки из официального мануала, а также пример реализации парсера для twitter. Справедливости ради, следует указать, что похожий пост уже присутствует на habrahabr, но на мой взгляд, содержит слишком малое количество информации. Кого заинтересовала данная тема, добро пожаловать под кат.

Получение html кода страницы

$html = file_get_html('http://habrahabr.ru/'); //работает и с https://

Товарищ Fedcomp дал полезный комментарий насчет file_get_contents и 404 ответа. Оригинальный скрипт при запросе к 404 странице не возвращает ничего. Чтобы исправить ситуацию, я добавил проверку на get_headers. Доработанный скрипт можно взять тут.

Поиск элемента по имени тега

foreach($html->find('img') as $element) { //выборка всех тегов img на странице
       echo $element->src . '<br>'; // построчный вывод содержания всех найденных атрибутов src
}

Модификация html элементов

$html = str_get_html('<div id="hello">Hello</div><div id="world">World</div>'); // читаем html код из строки (file_get_html() - из файла)
$html->find('div', 1)->class = 'bar'; // присвоить элементу div с порядковым номером 1 класс "bar"
$html->find('div[id=hello]', 0)->innertext = 'foo'; // записать в элемент div с id="hello" текст foo

echo $html; // выведет <div id="hello">foo</div><div id="world" class="bar">World</div>

Получение текстового содержания элемента (plaintext)

echo file_get_html('http://habrahabr.ru/')->plaintext; 

Целью статьи не является предоставить исчерпывающую документацию по данному скрипту, подробное описание всех возможностей вы можете найти в официальном мануале, если у сообщества возникнет желание, я с удовольствием переведу весь мануал на русский язык, пока же приведу обещанный в начале статьи пример парсера для twitter.

Пример парсера сообщений из twitter

require_once 'simple_html_dom.php'; // библиотека для парсинга
            $username = 'habrahabr'; // Имя в twitter
            $maxpost = '5'; // к-во постов
            $html = file_get_html('https://twitter.com/' . $username);
            $i = '0';
            foreach ($html->find('li.expanding-stream-item') as $article) { //выбираем все li сообщений
                $item['text'] = $article->find('p.js-tweet-text', 0)->innertext; // парсим текст сообщения в html формате
                $item['time'] = $article->find('small.time', 0)->innertext; // парсим время в html формате
                $articles[] = $item; // пишем в массив
                $i++;
                if ($i == $maxpost) break; // прерывание цикла
            }
Вывод сообщений

                for ($j = 0; $j < $maxpost; $j++) {
                    echo '<div class="twitter_message">';
                    echo '<p class="twitter_text">' . $articles[$j]['text'] . '</p>';
                    echo '<p class="twitter_time">' . $articles[$j]['time'] . '</p>';
                    echo '</div>';
                }

Благодарю за внимание. Надеюсь, получилось не очень тяжеловесно и легко для восприятия.

Похожие библиотеки

htmlSQL — спасибо Chesnovich
Zend_Dom_Query — спасибо majesty
phpQuery — спасибо theRavel
QueryPath — спасибо ZonD80
The DomCrawler(Symfony) — спасибо choor
CDom — спасибо автору samally
Небезызвестный XPath — спасибо за напоминание KAndy

P.S.
Хаброжитель Groove подсказал что подобные материалы уже были
P.P.S.
Постараюсь в свободное время собрать все библиотеки и составить сводные данные по производительности и приятности использования.

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Должностная инструкция электромонтера опс 6 разряда
  • Крепыш удобрение для рассады инструкция по применению водорастворимое
  • Инструкция правила безопасного поведения в общественных местах
  • Starmix isc l 1425 basic инструкция
  • Амбробене сироп инструкция детям после 3 лет