Цикл "зацикливается"

e_moon

Новичок
Цикл "зацикливается"

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

Привожу код моего скрипта, который отработав минуты три (при этом процессор начинает страшно гудеть ппц!) вырубается, не выдав ни каких предупреждений. Если жду загрузки в Opera выдает, что "Соединение закрыто сервером".

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

PHP:
set_time_limit(0);
ini_set('max_execution_time',0);
ignore_user_abort(True);

class Calc
{
	var
	$Data=Array();

	function GET($URL)
	{
		if(!preg_match('/^(f|ht)tp(s?):\/\/'.$_POST['domain'].'/',$URL)) 
		return Null;
		
		else
		...
		return $HTML;
	}

	function finedLinks($HTML)
	{
		preg_match_all('/<a.*?href=\"?\'?([^"\'>]+)\"?\'?.*?>/i',$HTML,$Buff);
		if(isset($Buff[1][0]))
		{
			foreach($Buff[1] AS $Key=>$Value)
			{
				if($Value)
				{
					print ' '; //Печатаю для браузера, чтоб не зависал
					if(in_array($Value,$this->Data)) continue;
					$this->Data[]=$Value;
					$this->finedLinks($this->GET($Value));
				}

				unset($Value);
			}
		}
		unset($Buff);
	}
}

$test=new Calc;
$test->finedLinks($test->GET($_POST['domain']));
print_r($test->Data);

$fp=fopen('buff.txt','w+');
fputs($fp,implode("/r/n",$test->Data));
fclose($fp);
 

Вурдалак

Продвинутый новичок
Если на какой-либо странице, готовящейся к такой своеобразной "индексации", будет ссылка на уже "проиндексированную", то и будет зацикливание.

-~{}~ 09.12.09 13:28:

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

e_moon

Новичок
Если на какой-либо странице, готовящейся к такой своеобразной "индексации", будет ссылка на уже "проиндексированную", то и будет зацикливание.
Простой проверки недостаточно?
PHP:
if(in_array($Value,$this->Data)) continue;
А сдох скрипт, скорее всего, от переполнения памяти, т.к. используется рекурсивный алгоритм.
Т.е. получается, что функция сама себя размножает и заполняет всю доступную память? Но как тогда рекурсию обойти?
 

varan

Б̈́̈̽ͮͣ̈Л̩̲̮̻̤̹͓ДͦЖ̯̙̭̥̑͆А͇̠̱͓͇̾ͨД͙͈̰̳͈͛ͅ
if(in_array($Value,$this->Data)) continue;
Вроде всё должно быть нормально.
Видимо или ссылок много или в функции GET косяк.

В любом случае, требуется отладка, чтобы понять, где сбой.
 

Вурдалак

Продвинутый новичок
Автор оригинала: e_moon
Простой проверки недостаточно?
PHP:
if(in_array($Value,$this->Data)) continue;
Достаточно, если ты уверен, что нигде не используются ссылки с псевдослучайными значениями. SID'ы в URL, ещё что-то.

Автор оригинала: e_moon
Т.е. получается, что функция сама себя размножает и заполняет всю доступную память? Но как тогда рекурсию обойти?
Переписать под итеративный вариант.

http://phpfaq.ru/debug
 

e_moon

Новичок
Достаточно, если ты уверен, что нигде не используются ссылки с псевдослучайными значениями. SID'ы в URL, ещё что-то.
Уверен.

Видимо или ссылок много
Скорее всего это.

Попробовал через Expert Debugger - отработал до конца без ошибок. Но как мне показалось он очищает память на остановках. А из скрипта в процессе работы очистить память не получится:(

Может есть какие альтернативы рекурсии?
Никогда не пробовал, но что думаю, если в цикле через fsockopen запускать скрипт с фунцкией?Не пройдет такой вариант?
 

varan

Б̈́̈̽ͮͣ̈Л̩̲̮̻̤̹͓ДͦЖ̯̙̭̥̑͆А͇̠̱͓͇̾ͨД͙͈̰̳͈͛ͅ
пиши memory_get_usage в лог по ходу пьесы.

А вообще, можно так:

хранить массив урлов вместе с флагами (урл отработан/не отработан), можно хранить в mysql, если их очень много и надо делать за несколько запусков скрипта.

И цикл, в котором берется следующий урл из массива, обрабатывает его, ставит флаг обработанности, добавляет новые ссылки если они новые.
 

e_moon

Новичок
пиши memory_get_usage в лог по ходу пьесы.
К сожалению в наличии не имеется.
хранить массив урлов вместе с флагами (урл отработан/не отработан), можно хранить в mysql, если их очень много и надо делать за несколько запусков скрипта.
Скрипт в действительности ссылки заносит в базу.
$this->Data привел, чтобы упростить пример.

И цикл, в котором берется следующий урл из массива, обрабатывает его, ставит флаг обработанности, добавляет новые ссылки если они новые.
Я так и храню: url -> status (http://.../page.html -> 200). И проверяю перед добавлением. Плюс в базе стоит индекс Unique на поле url.
Или Вы имели в виду сначала отсканировать (заносить все подряд), а затем проверять? Но так все-равно зависает, т.к. рекурсия не упрощается.

Видимо все же вопрос в памяти, которой не хватает.
Сейчас попробовал два варианта:

1-й такой: Создал глобальный счетчик, который, при каждом вызове функции из цикла, увеличиваю на +1. После завершения - уменшаю на -1. При запуске соответственно проверяю, если счетчик заполнен (допустим 10) - выход. Но отсканировал скрипт не все.

2-й вариант: Убрал рекурсию в цикле вообще. После 1-го вызова функции запускаю while(isset($this->Data[$i++])). Ну а функция в процессе дописывает в массив новые ссылки. Вроде сработал до конца, все нашел (хотя гудит все-равно как трактор!).

Вообщем буду думать дальше, если появятся какие варианты или мысли, поделитесь.
 

Adelf

Administrator
Команда форума
Не процессор у тебя гудит, успокойся :)

Всем тем, кто "парсит контент" - учитесь :) ключевое слово "SiteMap".

А если по теме: http://phpfaq.ru/debug
 

varan

Б̈́̈̽ͮͣ̈Л̩̲̮̻̤̹͓ДͦЖ̯̙̭̥̑͆А͇̠̱͓͇̾ͨД͙͈̰̳͈͛ͅ
Или Вы имели в виду сначала отсканировать (заносить все подряд), а затем проверять? Но так все-равно зависает, т.к. рекурсия не упрощается.
Рекурсию вообще убрать, просто цикл до тех пор пока в массиве не кончатся урлы.

Кстати, в коде, который ты привел в самом начале главная проблема с памятью в том, что ты html страницы держишь в переменной внутри рекурсивной функции ($HTML). Если бы ты сделал отельную функцию куда передавал бы url страницы, а в ответ получал бы список урлов, то было бы всё гораздо лучше. Тут всё дело в том, что переменные внутри функции уничтожаются только при завершении работы функции, а в случае с рекурсией, завершение работы функции происходит очень не скоро.
 

e_moon

Новичок
Кстати, в коде, который ты привел в самом начале главная проблема с памятью в том, что ты html страницы держишь в переменной внутри рекурсивной функции ($HTML). Если бы ты сделал отельную функцию куда передавал бы url страницы, а в ответ получал бы список урлов, то было бы всё гораздо лучше. Тут всё дело в том, что переменные внутри функции уничтожаются только при завершении работы функции, а в случае с рекурсией, завершение работы функции происходит очень не скоро.
Да, я уже работаю в этом направлении. В целом все получается прекрасно.
Спасибо!
 
Сверху