Skip to Content

Парсер на основе simple_html_dom.php, защита от разрыва связи.

14 всего / 0 новых
Последнее
sasha_ua
Молчун
Сообщения: 8
Бонусы: 1
Пол: Мужской
Группы: Нет
Блог: читать!
Парсер на основе simple_html_dom.php, защита от разрыва связи.

Здравствуйте.

Изучение программирования начал с написания парсера(PHP) интернет-магазина.
Предварительно прочитав Машины статьи..

Собственно схема парсинга:
1. Парсим ссылки на разделы
2. Заходим по каждой ссылке и парсим ссылки на страницы разделов
3. Парсим необходимые данные с каждой страницы
4. Парсим ссылки на подробное описание каждого товара
5. Парсим подробное описание

В общем проблема в том что по пункту 2 процедура длится 50мин, выдает 1100стр, на каждой странице по 10 позиций, итого 11000 страниц с подробным описанием.
Запустил я скрипт на локальном сервере(сняв временное ограничение на выполнение) и поехал по своим делам, приезжаю коннект разорван, вся работа впустую..

Хотелось бы знать как можно реализовать защиту от дисконнекта?
Может в самой библиотеке есть какие-то временные константы?

Еще вопросы:
1. Непонятно как писать на пхп парсеры, ведь задачи могут выполняться часами, а на серверах провайдеров ограничения по 30сек.
2. Даже если снять такие ограничения скрипты виснут..

BOLVERIN (не проверено)
Группы: Нет
Блог: читать!

есть такие штуки как cron и AJAX. при первом ставим ограничение на кол-во итераций на один запуск. при втором просто запускаем цикл на аджаксах и юзаем опять же ограничение на кол-во итераций для каждого запуска, но запускать выполнение можно сразу в несколько потоков
где хрнаите полученную инфу? надеюсь не в массивах?

sasha_ua
Молчун
Сообщения: 8
Бонусы: 1
Пол: Мужской
Группы: Нет
Блог: читать!

У меня Винда и до Ajax руки еще долго не дойдут, я только за ПХП взялся..

Информацию заношу в локальную БД mysql.

BOLVERIN
Разговорчивый
Сообщения: 20
Бонусы: 18
Пол: Мужской
Группы: Нет
Блог: читать!

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

sasha_ua
Молчун
Сообщения: 8
Бонусы: 1
Пол: Мужской
Группы: Нет
Блог: читать!

Насчет Ajax, надо подумать..
Но основная проблема в разрывах связи, вот фрагмент кода:

/*-------------- 2.Получаем список страниц всех разделов --------------*/
function get_page_link($start, $end)
{
global $homedir;
$id=1;
/*------------ Извлекаем кол-востраниц ---------------*/

for ($i=$start; $i<=$end; $i++)
{
sleep(1); // поставлю секундную задержку на всякий пожарный
$current_page = get_parts_path($i);

if(strpos($current_page,"site.ua"))
{
echo "".$current_page.""."\n"; // Извлекаем ссылку на раздел
$html = file_get_html($current_page); // Создаем объект DOM из N-страницы

if($html) // Если создан DOM-объект обрабатываем (проверка на соединение), если нет, i--, т.е. циклим
{
$link_list = $html->find('div[class="listing"]'); // Извлекаем div.listing, их 2шт, нам нужен один.
$one_list = $link_list[0]; // $link_list(массив), $one_list(строка), получаем только первый div
$list_dom = str_get_html("$one_list"); // Преобразуем строку в обьект DOM
$str = $list_dom->find('a', 0); // Ищем ссылки в div.listing. Если не ноль выводим их циклически
// Если ноль, выводим исходную ссылку (полученную при парсинге разделов).

$next_page_link = iconv("utf-8","windows-1251",'следующая'); // Преобразовываем слово 'следующая' в cp1251

if($str) // Если в блоке пагинации найдены ссылки, перебираем, заносим в БД, за исключением 'Следующей'
{
foreach ($list_dom->find('a') as $one_list) // Извлекаем все ссылки из DOM обьекта с тэгами
{

// Добавить проверку на содержание в строке слова "следующая"
// Если найдена ничего не делать, если найдена - вывести текущую ссылку

$q = $one_list->innertext; // q - текст внутри ссылки

if($q==$next_page_link) // Если ссылка не "Следующая", заносим ссылку в БД
{
}
else
{
$f = $homedir.$one_list->href;
echo $f."\n";

// Заносим ссылки страниц в БД, id добавляется автоматически
$chq='insert into pagelink(link_pages,path,parent_id) values(\''.addslashes(getTextFromHTML($f)).'\',\''.$current_page.'\',\''.$i.'\')';
mysql_query($chq);
}
}
}
else
{
echo $current_page."\n"; // Выводим первую и единственную страницу
$chq='insert into pagelink(link_pages,path,parent_id) values(\''.addslashes(getTextFromHTML($current_page)).'\',\''.$current_page.'\',\''.$i.'\')';
mysql_query($chq);
}
$html->clear(); // Очищаем DOM объекты
unset($html);
$list_dom->clear();
unset($list_dom);
}

else // Если DOM-объект не создан, т.е. соединение разорванно, циклим for
{
$i--;
echo "warning:disconnect";
}
}
}
}

Оказалось что условие:
if($html)
{}

Всегда TRUE
else {}
Не выполняется.

Идея такова - если объект DOM пустой, т.е. не сформирован, тогда соединение разорвано, и счетчик уменьшается на единицу, т.е. циклится пока соединение не восстановится..
Если например соединение рвется во время формирования ДОМ объекта, $html все равно заполнен.

sasha_ua
Молчун
Сообщения: 8
Бонусы: 1
Пол: Мужской
Группы: Нет
Блог: читать!

Вот заменил код:
$if_down = $html->find("/html",0); // Если найдено "/html" значит файл полностью загружен

if($if_down) // Если создан DOM-объект обрабатываем (проверка на соединение), если нет, i--, т.е. циклим
{
$link_list = $html->find('div[class="listing"]'); // Извлекаем div.listing, их 2шт, нам нужен один.
$one_list = $link_list[0]; // $link_list(массив), $one_list(строка), получаем только первый div
$list_dom = str_get_html("$one_list"); // Преобразуем строку в обьект DOM
$str = $list_dom->find('a', 0);

Работает почти правильно, осталось разобраться почему выводит:

Цитата:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 384 bytes) in C:\wamp\www\php\simple_html_dom.php on line 693

Сандер
Администратор
Сандер аватар
ГуруМодератор
Сообщения: 1944
Бонусы: 617
Пол: Мужской
Блог: читать!

Это нехватка памяти php.
Нужно в php.config увеличить ограничение по памяти

sasha_ua
Молчун
Сообщения: 8
Бонусы: 1
Пол: Мужской
Группы: Нет
Блог: читать!

Скорее не отчищаются ДОМ-элементы, хотя я писал:
// Очищаем DOM объекты
$html->clear();
unset($html);
$list_dom->clear();
unset($list_dom);

До изменения кода такого не было, может не туда вставил, я в коде еще плаваю Smile

BOLVERIN
Разговорчивый
Сообщения: 20
Бонусы: 18
Пол: Мужской
Группы: Нет
Блог: читать!

бедный ваш комп) такое кол-во интераций без разгрузки памяти и garbage collection
убивайте объекты после каждой итерации и следите за кол-вом памяти, а лучше сделайтне как я сказал:
либо с автозапуском по крону (вполне можно на простеньком хостинге реализовать) либо на AJAX

sasha_ua
Молчун
Сообщения: 8
Бонусы: 1
Пол: Мужской
Группы: Нет
Блог: читать!

1. Это не разгрузка?

Цитата:
$html->clear();
unset($html);
$list_dom->clear();
unset($list_dom);

2. Разве PHP не язык с автоматической GC, или это в теории?

3. Что даст использование Ajax, кроме регулирования времени выполнения скрипта?

Отредактировано пользователем sasha_ua в Вс, 17/10/2010 - 15:33.
BOLVERIN
Разговорчивый
Сообщения: 20
Бонусы: 18
Пол: Мужской
Группы: Нет
Блог: читать!

1. судя по симптомам память не чистится
2. вот этого я не знаю. скорее всего GC выполняется после завершения скрипта и все, а не во время выполнения его
3. AJAX не регулирует время исполнения скрипта. на аджаксах надо сделать просто обращение к скрипту через определенный период времени. в самом же скрипте надо задать лимит на кол-во итераций.
сделав на аджаксе вы просто запустите выполнение и сможете выполнять любую работу дальше при этом ваш комп не будет тормозить из-за работы скрипта и не будет проблем с обрывом связи так как браузерная часть AJAX будет просто посылать запросы к скрипту

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

sasha_ua
Молчун
Сообщения: 8
Бонусы: 1
Пол: Мужской
Группы: Нет
Блог: читать!

Насчет Ajax'a подумаю, но выкладывать на сервер хостера не решусь, т.к. там выделяется 2-3% от ЦПУ, меня забанят Smile

Пока решение такое: проверять полноту закачки стандартными ПХП функциями, а потом преобразовывать ее в ДОМ-объект с помощью библиотеки.

BOLVERIN
Разговорчивый
Сообщения: 20
Бонусы: 18
Пол: Мужской
Группы: Нет
Блог: читать!

если сделаете нормально с фронтенд\бекенд то жрать будет раз в 5 меньше чем вордпресс
а зачем преобразовывать в DOM-объект?

sasha_ua
Молчун
Сообщения: 8
Бонусы: 1
Пол: Мужской
Группы: Нет
Блог: читать!

Цитата:
а зачем преобразовывать в DOM-объект?

Я пользуюсь simple_html_dom.php

RSS-материал


Активные пользователи

ПользовательБонусы
Сандер617
Nefertity247
Tori242
robot201
Chief183
STAER140
faiters139
Mavarik121
akkadites94
staiki94