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

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

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

лучшие конструкторы сайтов

Оставлять комментарии могут только зарегистрированные пользователи

Комментарии  

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

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

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

Parse error: syntax error, unexpected T_VAR in H:\home\parser\www\01\index.php on line 42
Leroy
# Leroy 29.04.2014 10:58
Виктор Соседко
# Виктор Соседко 03.09.2016 17:43
Привет а можно как то в нём сделать что бы он парсил каждое в своё окно!То есть к примеру я хочу ним парсить описания к фильмам и мне нужно что бы описание было в одном окне а год выпуска например в другом можно ли такое?Кто может помогите это реализовать!
Андрей К
# Андрей К 06.10.2019 11:42
Не знаю, актуально ли еще (2016 год, все-таки), но я могу это сделать) Ответьте на мой коммент, если еще актуально
Romannsk
# Romannsk 06.12.2021 11:56
Добрый день. Нужен парсер новостей и статей заголовков и дополнительная оценка каждой новости по определенным параметрам
Андрей К
# Андрей К 06.10.2019 11:46
Цитирую Виктор Соседко:
Привет а можно как то в нём сделать что бы он парсил каждое в своё окно!То есть к примеру я хочу ним парсить описания к фильмам и мне нужно что бы описание было в одном окне а год выпуска например в другом можно ли такое?Кто может помогите это реализовать!

Цитирую Виктор Соседко:
Привет а можно как то в нём сделать что бы он парсил каждое в своё окно!То есть к примеру я хочу ним парсить описания к фильмам и мне нужно что бы описание было в одном окне а год выпуска например в другом можно ли такое?Кто может помогите это реализовать!

Цитирую Виктор Соседко:
Привет а можно как то в нём сделать что бы он парсил каждое в своё окно!То есть к примеру я хочу ним парсить описания к фильмам и мне нужно что бы описание было в одном окне а год выпуска например в другом можно ли такое?Кто может помогите это реализовать!

Цитирую Виктор Соседко:
Привет а можно как то в нём сделать что бы он парсил каждое в своё окно!То есть к примеру я хочу ним парсить описания к фильмам и мне нужно что бы описание было в одном окне а год выпуска например в другом можно ли такое?Кто может помогите это реализовать!


Не знаю, актуально ли еще, но я могу это сделать) Если еще надо - ответьте на мой комментарий
Andrew Cheater K
# Andrew Cheater K 06.10.2019 11:50
Цитирую Виктор Соседко:
Привет а можно как то в нём сделать что бы он парсил каждое в своё окно!То есть к примеру я хочу ним парсить описания к фильмам и мне нужно что бы описание было в одном окне а год выпуска например в другом можно ли такое?Кто может помогите это реализовать!


Здравствуйте! Не знаю, актуально ли еще), но если да - я могу это сделать. Ответьте на мой коммент, если интересно.
kasym
# kasym 29.04.2014 11:30
Здравствуйте!
Спасибо все работает!
axeld
# axeld 14.06.2014 17:18
Прикольный парсер, хорошо работает. Нужно только научить его выдёргивать сам контент страниц, без меню и прочего мусора. Попробую это сделать.
Игорь
# Игорь 13.12.2014 13:45
А у меня парсер умеет выдергивать произвольный контент страниц:

http://excelvba.ru/programmes/Parser

Только он не на PHP написан, а на VBA для Excel
MAYA
# MAYA 24.11.2019 22:10
Ну ты же истынный талпайоп
ЛВС
# ЛВС 20.08.2014 03:49
У меня парсер не работает. Чистая страница показывается. Даже форма с текстовым полем и кнопкой на странице не появляется. Т.к. в форме есть короткая запись php, глянул в php.ini параметр short_open_tag = On. Проверил и с помощью phpinfo.php, тоже short_open_tag = On. Два исходных файла к статье положил в localhost. Запускал localhost/index.php.
ЛВС
# ЛВС 21.08.2014 04:05
Вероятно, не работает из-за функции request, которую я не знаю, как подключить и куда подключить. Копировать эту функцию как она есть из статьи "Авторизация на сайте при помощи curl php" и вставлять в файл simple_html_dom.php? Сам cUrl я установил, phpinfo.php показывает, что блок curl есть.
Leroy
# Leroy 21.08.2014 12:08
в simple_html_dom.php блок ничего вставлять не надо. Этот файл - отдельная библиотека. Создайте другой, тот в котором весь парсер. Сделайте вывод ошибок http://xdan.ru/Vkljuchaem-pokaz-oshibok-v-php.html . Тогда станет понятно что за ошибка у вас
ЛВС
# ЛВС 22.08.2014 19:03
"­Создайте другой, тот в котором весь ­парсер". Не очень понятно. Другой файл создать? С каким расширением и что в нём писать? Парсер - это файл index.php, он же уже есть, зачем ещё один создавать. Извиняюсь за свою непонятливость.
ЛВС
# ЛВС 23.08.2014 05:05
Функция ­request уже прописана в index.php, её не нужно никуда подключать, лишь бы cUrl был установлен. Сейчас склоняюсь, что может мой интерпретатор php5 или apache неправильно сконфигурированы. Парсер вообще ничего не показывает, даже форму не показывает, хотя если в файле оставить только код формы, вырезав остальное, форма показываться будет.
Leroy
# Leroy 23.08.2014 22:11
включите показ ошибок и все увидите
ЛВС
# ЛВС 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
# Leroy 24.08.2014 21:08
причем тут pear? у вас просто не работает файл. вы hello world для начала напишите
ЛВС
# ЛВС 25.08.2014 07:28
hello world работает. И выполнение php в html в тестовом файле тоже работает. Вполне возможно, неисправность от того, что я под роутером сижу, там копать надо.
ЛВС
# ЛВС 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
# СтройРостов161 stroyrostov161 02.03.2015 16:56
полезно
СтройРостов161
Юрий
# Юрий 18.03.2015 13:11
Ищу человека, который может создать парсер новостей. Цель парсить текст новости и фото если есть, заливать к себе в БД согласно категорий. Разумеется не бесплатно, заинтересованные пишите на почту .
Elnazar Berdybaev
# 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
# Vovka 29.02.2016 22:39
Прочитал до конца и с интересом. Сохранил в закладки, да подписался. Для своего пригодится. Спасибо автору!
chuvyr.ru
# chuvyr.ru 21.05.2016 12:08
В принципе можно использовать стандартные PHP классы обработки XML. Например, SimpleXML или DOMDocument. Не нужно заморачиваться с дополнительными библиотеками.
Владимир Сковкин
# Владимир Сковкин 27.05.2016 19:25
Несмотря на дату.Статья познавательная.
Вот вопрос реально "Avito имеющие системы защиты от таких наглецов."
Сохранил стр с их сайта на денвер, вот я вижу нужный тег !
И вытаскиваю шаблоном preg_match_all - а шаблон его в тупую не видит ???
Кодировку выставлял ---
BSOD
# BSOD 14.06.2016 23:45
Спасибо большое, помогли решить проблему. Очень благодарен за статью
alexindacomp
# alexindacomp 28.08.2016 00:02
Simple HTML Dom годится только для парсинга идеального html. В других случаях он просто начинает жестко тупить. Другое дело phpparserplus.esy.es, который не виснет и парсит с помощью multi-curl да и интерфейс готовый. Проверен на сотни сайтов.
alexindacomp
# alexindacomp 28.08.2016 00:05
Simple HTML DOM годится только для идеалного html кода страницы, иначе начинает жестко тупить. Другое дело phpparserplus.esy.es, который работает с помощью multi-curl и проверен на сотни сайтах.
bdseo
# bdseo 01.09.2016 11:09
Спасибо, давно ищу лучшие парсеры на рнр.
Скажите, а phpQuery кто-то пользуется?
hdse.ru
# hdse.ru 21.10.2016 15:53
неплохая статья, можно еще и про парсер видео написать
Марина3125
# Марина3125 03.03.2017 13:00
сколько такой парсер стоит?
hi-tech новости!
# hi-tech новости! 28.06.2017 23:53
Очень хорошая статья, выручила! Надеюсь что вы автор будете писать больше полезных статей!
Андрей bdseo.ru
# Андрей bdseo.ru 13.12.2017 23:40
Спасибо за статью, почти 2018 год, а все также удобно.
Не планируете свежих статей в этом роде? Может что новое, быстрее появилось?
Tomash Braun
# Tomash Braun 25.03.2018 22:53
Удивительно, но тема парсеров не менее актуальна и сейчас. Спасибо автору за статью, пошел писать свой парсер.
Арни
# Арни 17.04.2019 09:18
Приветствую.
Подскажите пожалуйста, а если на сайте сначала нужно залогиниться, чтобы появилась информация, которую нужно спарсить. Как быть?
Tomash Braun
# Tomash Braun 28.07.2019 12:02
Спасибо за статью, но для чайников недостаточно разжёван сам алгоритм написания парсера. Если бы еще описать до кода весь алгоритм то было бы вообще супер статья.А так все равно спасибо за открытый код.
Person
# Person 09.09.2020 16:10
А этот может парсить любой сайт.
Бесплатный, легко настраиваемый без особых знаний за пару минут на любой сайт
Пример парсинга rozetka.com
Составление списка параметров товаров нужной категории и сохранение в файл. Настроить можно на любой сайт.
Видео https://youtu.be/TfAKu7O5zZo
por1122
# por1122 07.07.2022 18:04
Pgslot vs slotxo ทั้งสองค่ายเกมแตกต่างกันอย่างไร มีเกมอะไรที่น่าสนใจบ้างเรามาดูกัน การชนกันจาก 2 ค่ายเกมสล็อตออนไลน์ ชื่อดังสองค่าย ห้ำหั่นกันในเว็บของเรา PG-SLOT.GAME
Kamen
# Kamen 04.12.2022 16:59
jokergaming Easy to apply for membership with us, get free credits, free bonuses anytime, anywhere in top-up. There are promotions for both new and old members.
Deposit and withdraw with an automatic system, do not waste time waiting for a long time.