В аську постучал один из читателей этого блога, и заказал универсальный парсер сайтов. Парсер должен был уметь грабить произвольный сайт и выдирать из него всю текстовую информацию. Кроме того, он должен найти все ссылки на сайте и пройти по ним. У парсера должна быть настройка, ограничивающая число страниц, которое он парсит за один раз. Цена была небольшой, но и задание само по себе несложное. Итак, приступим

Для начала нам потребуется библиотека Simple HTML Dom и функция которая будет запрашивать данные(request).

Выведем форму ввода адреса донора и создадим каркас класса парсера

<form method="POST">
	<input name="url" type="text" value="<?=isset($_REQUEST['url'])?$_REQUEST['url']:'http://xdan.ru/parser/parser/test.html';?>"/><input type="submit" value="Пошел">
</form>
<?php

include 'simple_html_dom.php';
class parser{
	var $cacheurl = array();
	var $result = array();
	var $_allcount = 10;
	function __construct(){
		if(isset($_POST['url'])){
			$this->parse($_POST['url']);
		}
	}
	function parse($url){}
	function readUrl($url){}
	function printresult(){
		foreach($this->result as $item){
			echo '<h2>'.$item['title'].' - <small>'.$item['url'].'</small></h2>';
			echo '<p style="margin:20px 0px;background:#eee; padding:20px;">'.$item['text'].'</p>';
		};
		exit();
	}
}
$pr = new Parser();
$pr->printresult();

Парсить будет метод parse. В остальном все просто. 

_allcount 

содержит ту самую настройку, для ограничения количества скачанных страниц.

Напишем метод parse. Но перед этим, нам потребуется еще один метод. Он будет получать на вход $url и смотреть, если это локальный относительный  адрес (index.php) то добавлять в начало домен, если полный адрес, то запоминать папку в котором происходит парсинг и наконец если это полный адрес другого сайта, то возвращать false, потому как такие ссылки нам не нужны

var $protocol = '';
var $host = '';
var $path = '';
function readUrl($url){
	$urldata = parse_url($url);
	if( isset($urldata['host']) ){
		if($this->host and $this->host!=$urldata['host'])
			return false;
			
		$this->protocol = $urldata['scheme'];
		$this->host = $urldata['host'];
		$this->path = $urldata['path'];
		return $url;
	}
		
	if( preg_match('#^/#',$url) ){
		$this->path = $urldata['path'];
		return $this->protocol.'://'.$this->host.$url;
	}else{
		if(preg_match('#/$#',$this->path))
			return $this->protocol.'://'.$this->host.$this->path.$url;
		else{
			if( strrpos($this->path,'/')!==false ){
				return $this->protocol.'://'.$this->host.substr($this->path,0,strrpos($this->path,'/')+1).$url;
			}else 
				return $this->protocol.'://'.$this->host.'/'.$url;
		}
	}
}

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

не буду долго вас томить, метод parse прост, как две копейки

function parse($url){
	$url = $this->readUrl($url);
	
	if( !$url or $this->cacheurl[$url] or $this->cacheurl[preg_replace('#/$#','',$url)] )
		return false;
		
	$this->_allcount--;
	
	if( $this->_allcount<=0 )
		return false;
		
	$this->cacheurl[$url] = true;
	$item = array();	
	
	$data = str_get_html(request($url));
	$item['url'] = $url;
	$item['title'] = count($data->find('title'))?$data->find('title',0)->plaintext:'';
	$item['text'] = $data->plaintext;
	$this->result[] = $item;
	
	if(count($data->find('a'))){
		foreach($data->find('a') as $a){
			$this->parse($a->href);
		}
	}
	$data->clear();
	unset($data);
	
}

Сохраняем все ссылки в массиве cacheurl  для того чтобы не парсить один сайт два раза. уменьшаем счетчик ограничения.  Парим нужную страницу, сохраняем нужные данные. Находим все ссылки на ней, и рекурсивно вызываем ее же по этим ссылкам.

Парсер готов.

Разумеется, этот парсер не сможет парсить такие сайты, как Google, Yandex, Avito имеющие системы защиты от таких наглецов. Также он не сможет парсить сайты созданные на любом из лучших конструкторов сайтов. Сайты на эти хоть и очень простые, и сделаны новичками, но имеют под собой сложную инфраструктуру, которая не позволит просто так их парсить.   

Исходные файлы к статье 

лучшие конструкторы сайтов
Рассказать друзьям
author.jpg

Платная консультация по вопросам 1500 руб/час

Прочитали статью и остались вопросы? Меня зовут Валерий и я её автор. С радостью объясню Вам в скайпе все затруднительные моменты, которые остались за рамками статьи!

Подробнее ...

Добавить комментарий


Комментарии   

kasym
-1 # kasym 29.04.2014 04:41
Здравствуйте!

А могли бы вы прикрепить файлы с исходниками?

А то у меня выводит ошибку:

Parse error: syntax error, unexpected T_VAR in H:\home\parser\www\01\index.php on line 42
Ответить | Ответить с цитатой | Цитировать
Виктор Соседко
0 # Виктор Соседко 03.09.2016 17:43
Привет а можно как то в нём сделать что бы он парсил каждое в своё окно!То есть к примеру я хочу ним парсить описания к фильмам и мне нужно что бы описание было в одном окне а год выпуска например в другом можно ли такое?Кто может помогите это реализовать!
Ответить | Ответить с цитатой | Цитировать
kasym
0 # kasym 29.04.2014 11:30
Здравствуйте!
Спасибо все работает!
Ответить | Ответить с цитатой | Цитировать
axeld
-2 # axeld 14.06.2014 17:18
Прикольный парсер, хорошо работает. Нужно только научить его выдёргивать сам контент страниц, без меню и прочего мусора. Попробую это сделать.
Ответить | Ответить с цитатой | Цитировать
Игорь
-1 # Игорь 13.12.2014 13:45
А у меня парсер умеет выдергивать произвольный контент страниц:

http://excelvba.ru/programmes/Parser

Только он не на PHP написан, а на VBA для Excel
Ответить | Ответить с цитатой | Цитировать
ЛВС
0 # ЛВС 20.08.2014 03:49
У меня парсер не работает. Чистая страница показывается. Даже форма с текстовым полем и кнопкой на странице не появляется. Т.к. в форме есть короткая запись php, глянул в php.ini параметр short_open_tag = On. Проверил и с помощью phpinfo.php, тоже short_open_tag = On. Два исходных файла к статье положил в localhost. Запускал localhost/index.php.
Ответить | Ответить с цитатой | Цитировать
ЛВС
0 # ЛВС 21.08.2014 04:05
Вероятно, не работает из-за функции request, которую я не знаю, как подключить и куда подключить. Копировать эту функцию как она есть из статьи "Авторизация на сайте при помощи curl php" и вставлять в файл simple_html_dom.php? Сам cUrl я установил, phpinfo.php показывает, что блок curl есть.
Ответить | Ответить с цитатой | Цитировать
Leroy
0 # Leroy 21.08.2014 12:08
в simple_html_dom.php блок ничего вставлять не надо. Этот файл - отдельная библиотека. Создайте другой, тот в котором весь парсер. Сделайте вывод ошибок http://xdan.ru/Vkljuchaem-pokaz-oshibok-v-php.html . Тогда станет понятно что за ошибка у вас
Ответить | Ответить с цитатой | Цитировать
ЛВС
0 # ЛВС 22.08.2014 19:03
"­Создайте другой, тот в котором весь ­парсер". Не очень понятно. Другой файл создать? С каким расширением и что в нём писать? Парсер - это файл index.php, он же уже есть, зачем ещё один создавать. Извиняюсь за свою непонятливость.
Ответить | Ответить с цитатой | Цитировать
ЛВС
0 # ЛВС 23.08.2014 05:05
Функция ­request уже прописана в index.php, её не нужно никуда подключать, лишь бы cUrl был установлен. Сейчас склоняюсь, что может мой интерпретатор php5 или apache неправильно сконфигурированы. Парсер вообще ничего не показывает, даже форму не показывает, хотя если в файле оставить только код формы, вырезав остальное, форма показываться будет.
Ответить | Ответить с цитатой | Цитировать
Leroy
+2 # Leroy 23.08.2014 22:11
включите показ ошибок и все увидите
Ответить | Ответить с цитатой | Цитировать
ЛВС
0 # ЛВС 24.08.2014 15:00
Спасибо! Показ ошибок был выключен. Благодаря указанной статье включил этот показ, вот, что показывает:

Warning: Unknown: failed to open stream: Permission denied in Unknown on line 0

Fatal error: Unknown: Failed opening required '/var/www/index.php' (include_path='.:/usr/share/php:/usr/share/pear')

in Unknown on line 0

В php.ini раскомментировал include_path = ".:/usr/share/php", перезапустил, получилось:

Warning: Unknown: failed to open stream: Permission denied in Unknown on line 0

Fatal error: Unknown: Failed opening required '/var/www/index.php' (include_path='.:/usr/share/php') in Unknown on line 0

По ходу выяснил, что на компе нет pear.
Ответить | Ответить с цитатой | Цитировать
Leroy
-1 # Leroy 24.08.2014 21:08
причем тут pear? у вас просто не работает файл. вы hello world для начала напишите
Ответить | Ответить с цитатой | Цитировать
ЛВС
-1 # ЛВС 25.08.2014 07:28
hello world работает. И выполнение php в html в тестовом файле тоже работает. Вполне возможно, неисправность от того, что я под роутером сижу, там копать надо.
Ответить | Ответить с цитатой | Цитировать
ЛВС
0 # ЛВС 06.09.2014 15:58
Парсер работает! Всё дело было в недостаточности прав, Ваши два файла имели права: -rw------- 1 root root, а остальные файлы: -rw-r--r-- 1 root root. Сделал для Ваших файлов права -rw-r--r-- 1 root root, и всё заработало. У меня ОС Kali, в ней по умолчанию работают из-под рута, потому и не смотрел права на файлы - я же root, хотя и знал, что из-за прав могут быть проблемы. Излишняя самоуверенность меня подвела. Вероятно, такие первоначальные права на Ваши файлы у меня получились из-за того, что я их распаковал на другом компе, а потом с помощью флешки перенёс на свой комп. В-общем, работает. Спасибо за парсер!
Ответить | Ответить с цитатой | Цитировать
СтройРостов161 stroyrostov161
-1 # СтройРостов161 stroyrostov161 02.03.2015 16:56
полезно
СтройРостов161
Ответить | Ответить с цитатой | Цитировать
Юрий
-1 # Юрий 18.03.2015 13:11
Ищу человека, который может создать парсер новостей. Цель парсить текст новости и фото если есть, заливать к себе в БД согласно категорий. Разумеется не бесплатно, заинтересованные пишите на почту .
Ответить | Ответить с цитатой | Цитировать
Elnazar Berdybaev
-1 # Elnazar Berdybaev 19.05.2015 19:00
выводит такую запись:
Notice: Undefined index: path in C:\xampp\htdocs\parser\index.php on line 85

а в строке 85 : $this->path = $urldata['path'];
Ответить | Ответить с цитатой | Цитировать
Vovka
-1 # Vovka 29.02.2016 22:39
Прочитал до конца и с интересом. Сохранил в закладки, да подписался. Для своего пригодится. Спасибо автору!
Ответить | Ответить с цитатой | Цитировать
chuvyr.ru
-1 # chuvyr.ru 21.05.2016 12:08
В принципе можно использовать стандартные PHP классы обработки XML. Например, SimpleXML или DOMDocument. Не нужно заморачиваться с дополнительными библиотеками.
Ответить | Ответить с цитатой | Цитировать
Владимир Сковкин
+1 # Владимир Сковкин 27.05.2016 19:25
Несмотря на дату.Статья познавательная.
Вот вопрос реально "Avito имеющие системы защиты от таких наглецов."
Сохранил стр с их сайта на денвер, вот я вижу нужный тег !
И вытаскиваю шаблоном preg_match_all - а шаблон его в тупую не видит ???
Кодировку выставлял ---
Ответить | Ответить с цитатой | Цитировать
BSOD
-1 # BSOD 14.06.2016 23:45
Спасибо большое, помогли решить проблему. Очень благодарен за статью
Ответить | Ответить с цитатой | Цитировать
alexindacomp
-1 # alexindacomp 28.08.2016 00:02
Simple HTML Dom годится только для парсинга идеального html. В других случаях он просто начинает жестко тупить. Другое дело phpparserplus.esy.es, который не виснет и парсит с помощью multi-curl да и интерфейс готовый. Проверен на сотни сайтов.
Ответить | Ответить с цитатой | Цитировать
alexindacomp
-1 # alexindacomp 28.08.2016 00:05
Simple HTML DOM годится только для идеалного html кода страницы, иначе начинает жестко тупить. Другое дело phpparserplus.esy.es, который работает с помощью multi-curl и проверен на сотни сайтах.
Ответить | Ответить с цитатой | Цитировать
bdseo
-1 # bdseo 01.09.2016 11:09
Спасибо, давно ищу лучшие парсеры на рнр.
Скажите, а phpQuery кто-то пользуется?
Ответить | Ответить с цитатой | Цитировать
hdse.ru
-1 # hdse.ru 21.10.2016 15:53
неплохая статья, можно еще и про парсер видео написать
Ответить | Ответить с цитатой | Цитировать
Марина3125
-1 # Марина3125 03.03.2017 13:00
сколько такой парсер стоит?
Ответить | Ответить с цитатой | Цитировать
hi-tech новости!
0 # hi-tech новости! 28.06.2017 23:53
Очень хорошая статья, выручила! Надеюсь что вы автор будете писать больше полезных статей!
Ответить | Ответить с цитатой | Цитировать
Андрей bdseo.ru
0 # Андрей bdseo.ru 13.12.2017 23:40
Спасибо за статью, почти 2018 год, а все также удобно.
Не планируете свежих статей в этом роде? Может что новое, быстрее появилось?
Ответить | Ответить с цитатой | Цитировать