rolling-curl
13 мая 2010
rolling-curl — PHP-класс для параллельного выполнения большого количества асинхронных HTTP-запросов при помощи curl, написанная Josh Fraser и поддерживаемая на данный момент мной.
Правильно чистит память, не простаивает зря, выполняя одновременно заданное число запросов. Обрабатывает каждый ответ сразу после выполнения запроса.
Пример:
// URL, которые будем обрабатывать $urls = array( "http://www.google.com", "http://www.facebook.com", "http://www.yahoo.com", ); // функция для обработки ответа function request_callback($response, $info) { // получаем title страницы if (preg_match("~<title>(.*?)</title>~i", $response, $out)) { $title = $out[1]; } echo "<b>$title</b><br />"; print_r($info); echo "<hr>"; } $rc = new RollingCurl("request_callback"); // одновременно позволим не более 20 запросов $rc->window_size = 20; foreach ($urls as $url) { // добавляем запросы в очередь $request = new RollingCurlRequest($url); $rc->add($request); } // запускаем $rc->execute();
Комментарии RSS по email OK
Спасибо :) Возьму на заметку :)
Как скачать?
Удобная вешь+ еще и на curl. А по асинхроным серверам на php ничего не завалялось случайно ?
Спасибо, возможно пригодится :)
Ewg
Можно забрать из SVN Google Code.
Максим
У меня лично по асинхронным нет, но есть http://github.com/kakserpom/phpdaemon.
Зачем
если можно
Вообще довольно странная библиотека...
И правда что-то не так
Почему то при запуске example.php функции curl_multi_exec и curl_multi_info_read у меня запускались по 94000 с лишним раз.
Что то там явно не так. Будто цикл какой-то крутится без таймаутов.
Там вот эта последовательность
Очень много раз повторяется (по 20-100 тыс раз)
Я бы не советовал этот класс использовать..
Да и ничуть оно ни асинхронное/неблокирующее.
Если в example.php в самом конце после $rc->execute(); вставить например print "Hello world"; то если все действительно асинхронно, то сперва напечатается "Hello world" а потом уже все остальное а тут сперва отрабатывает полностью $rc->execute(); (т.е. оно заблокировало все операции) и только потом print
Так что асинхронностью тут и не пахнет. Многопоточность разве что...
http://github.com/kakserpom/phpdaemon - ыыы, Данко! Я этого психа знаю лично - он на демонах на PHP собаку съел, целую стаю, стаж у него с этим лет 6-7 точно. Так что это реально хорошее решение будет, от опытного спеца :)
Сергей
Слово «асинхронные» тут относится именно к запросам и их обработке, а не к библиотеке в целом.
Советую почитать про curl_multi_exec и curl_multi_info_read.
Sam
Ок, насчет асинхронности в целом согласен, хотя к библиотеке в целом это не очень подходит.
Но то, что там в какой-то момент начинает крутиться цикл по 20-90 тыс итераций это явно проблема.
Сергей
Для curl_multi это совершенно нормально и указано в описаниях к методам.
Спасибо, но не пойму вот чего – я хочу сделать 5 миллионов запросов в 5 потоков. Все 5 миллионов я в $requests, понятное дело, не засуну – память не резиновая. Хотелось бы добавлять запросы в коллбэке по мере их выполнения. Однако приходится дождаться выполнения всех 5 запросов в первом потоке, и затем запускать следующий поток.
Либо делать множество более коротких очередей – скажем, 5 тысяч раз по 1000 урлов. Что уже лучше, но усложняет логику. Есть выход?
В самой библиотеке выхода, думаю, нет. Занесите в тикет, надо будет подумать.
Может ли один из урлов завесить выполнение всех запросов? Если скажем в списке урлов не яхо, а вася.пупкин.цюрюпинск.херсон.уа и на этом сервере днс сначала 3 минуты думает, а потом не отвечает и до хттп соотсветственно дело даже не доходит или доходит, но сервер выдаёт мегабайтный файл по 1 байту в секунду? :)
Нет, не может.
Возникла пара вопросов:
1) при работе скрипта (параллельно логинишься на два сайта; после этого параллельно забирается контент одной нужной страницы с каждого сайта) процес апача грузит проц на 50%. Это нормально или проблема конкретно у меня? ОС - виндовсХР, пакет - денвер+расширения, $rc->execute() - чтобы залогиниться и куки получить, второй $rc->execute()- чтобы получить контент.
2) Хотелось бы, чтобы после того как залогинишься на сайте, сразу начинал работать другой запрос для получения контента страницы, и после получения парситься, независимо от запросов к другим сайтам. Такое возможно с данным функционалом?
Привет!
Спасибо за либу. Очень пригодилось.
Есть вопрос.
С учетом того, что:
запросов допустим 1000, а
, то либа должна отрабатывать каждые 3 сек минимум 20 запросов. Независимо от скорости получения контента (ибо CURLOPT_TIMEOUT => 3). Значит на всю операцию должно уйти не более 150 сек. Добавим 150 сек для прочих операций. Итого 5 мин. Однако, на деле может и час и два работать. Такое впечатление, что после первых 20 потоков переходит в режим однопоточности + не работает CURLOPT_TIMEOUT.
С чем может быть связано?
в догонку к предыдущему посту
как-то эти цифры не совместимы с
версия curl libcurl/7.19.6
может дело в самой либе curl?
Jei
1) Не уверен. У меня не наблюдается.
2) Да, в коллбэках писать надо.
Turbonist
Скорее всего в curl. Параметры либе вроде передаются.
на кодах в вопросах, 12 вопрос, чел тоже испытывает проблемы с тем же самым.
вообще таймаут - это единственное, что пока не позволяет поменять сокеты на курл.
Пробую использовать в библиотеке список (массив) проксей - если урл не сработал с текущей проксей, необходимо заново поставить этот урл в очередь "скачиваний" со следующей по порядку проксей.
Создавать еще один новый объект RollingCurl и запускать еще один execute не хочется, т.к., вероятно, одновременных процессов уже будет не window_size, а больше.
Если делать $rc->add($request), то как лучше его перезапустить с учетом того, что часть запросов уже была отработана и второй раз делать их же нелогично? Сразу же заново запускать $rc->execute()? Или $rc->add($request) добавит урл с новой проксей в существующую очередь? Может ли получится так, что execute полностью выполнится до того, как будет добавлена урл с новой проксей и как это избежать?
P.S. Проверку, сработала ли прокси, пока планирую делать в функции request_callback примерно таким образом: if ($info['http_code']==501|502|503|504|505) {...}
Еще один вопрос...
Подскажите, пожалуйста, как настроить паузу между отправкой запросов на удаленный сервер, чтобы распределить его нагрузку во времени, т.е. чтобы между запусками отдельных curl было не менее nn секунд.
С очередями пока есть определённые проблемы. И вроде как есть решение. Надо будет его обкатать.
Пауз между запросами пока не предусмотрено. Нагрузку можно уменьшить выставив размер окна в единичку.
Судя по обсуждениям в форумам curl_multi_exec, возможно, это можно сделать добавлением usleep в RollingCurl.php примерно так:
Если гипотеза подтвердиться, пожалуйста, проапдейте RollingCurl.php.
Есть подозрение, что
usleep()
затормозит все потоки.Sam, а можно ли сделать так, чтобы каждый запрос через свою проксю ходил?
Можно. См.
RollingCurlGroup
.Здравствуйте! У меня приведенный пример не работает, выводится ошибка Fatal error: Class 'Request' not found in P:\home\localhost\www\multicurl\testRollingCurl.php on line 114 а если я вместо строки $request = new Request($url); пишу строку $rc->request($url); то скрипт работает неправильно. у меня в массиве порядка 90 адресов url, в настройках я указываю $rc->window_size = 10; скрипт выводит всего 6 результатов. Подскажите пожалуйста, где искать ошибку, т.к. я только учусь.
Request
в новых версиях переименован вRollingCurlRequest
.Не могли бы вы объяснить новичку, как без смены прокси и юзерагента, скрипт является неблокируемым?
замерял время выполнения 2 скриптов 1: при помощи RollingCurl запросить 10 страниц с удаленного сервера (в 10 потоков) 2: то же самое, но при помощи обычного curl в 1 поток.
результат - прирост скорости ~20% в пользу RollingCurl
2Sasha
Для подмены useragent и proxy подойдёт AngryCurl - улучшенная версия Rolling Curl на php.
Что-то не хочет у меня пример работать. В $response - NULL :( Подскажите плз, в чем проблема
Каким образом можно присоединиться к поддержке?
Снимаю свой вопрос. Вот более доработанная версия с композером и psr0
github.com/chuyskywalker/rolling-curl
Делали бы уж сразу PSR-4.
Здравствуйте! Скрипт RollingCurl еще поддерживаете?
Нет.
Можете порекомендовать что-то подобное?
Нет. Я давно не занимался этой темой.
$rollingCurl = new RollingCurl();
foreach ($DOMXPath->query('//a[contains(@class, "thumbnail-item__wrap")]/@href') as $linkToPageWithCamera) { if (isset($linkToPageWithCamera->nodeValue)) { $results[] = $linkToPageWithCamera->nodeValue; $counter++; } else { $this->addError("[ " . $request->getResponseErrno() . "]" . $request->getResponseError()); return; } } } }) ->setSimultaneousLimit(1000) ->execute(); echo "All results = " . print_r($results) . PHP_EOL;
Вопрос (поучаствовать могут все желающие) - Почему вызов коллбэка будет бесконечным (постоянно будет слать урлы вида
www.insecam.org/en/bycity/Aigaleo/?page=1) www.insecam.org/en/bycity/Aigaleo/?page=1?page=1 www.insecam.org/en/bycity/Aigaleo/?page=1?page=1?page=1
Хотя в коллбэке есть условие if ($k < $countOfPagesForCurrentCity[1] считаем количество ссылок в пагинаторе и в каллбэке вызываем подстановку соотвествующего урла для каждой базовой ссылки
Что я делаю не так, почему цикл бескнечный?
Нет, вот, Саш, ссылка на pastebin/ Так по понятнее будет.
pastebin.com/SVRzyv2D
Вопрос прежний, почему уходит в бесконечность цикл?
Есть страница, которую надо распарсить у нее есть пагинация
На первых итерациях парсер работает верно, генерирует страниццы например, someurl.ru/?page=1 someurl.ru/?page=2 someurl.ru/?page=3
То есть счетчик работает правильно.
Но когда нужно переключиться на другой урл то он начинает вместо перехода по условию (пока количество элементов в пагинации не меньше $i++) генерировать урлы вида
someurl.ru/?page=1?page=1 someurl.ru/?page=2?page=1 someurl.ru/?page=3?page=1
Что Я делаю не так? Или метод
$rollingCurl->get($request->getUrl() . '?page=' . $k); багует?
Спасибо.
Были у меня похожие проблемы с этим скриптом. Я его уже не использую. Нашел другую версией , более удобную и понятную для меня в плане механизма работы. github.com/2naive/AngryCurl
Paul, вы в конец параметр дописываете. А надо оригинальный URL распарсить и параметр страницы заменить.
Подскажите плз в каком методе формируется количество запросов в секунду. У меня есть ограничение на количество запросов в секунду и запросов в час. Хочу поставить тайм-аут но что то не могу понять в каком методе это можно реализовать
Подскажите, как подключить в Codeigniter?
По идее, так же, как и без CodeIgniter.