Мульти HTTP-запрос на CURL
Иногда возникает необходимость во время исполнения скрипта сделать несколько http запросов. При большом их количестве возникает проблема со временем обработки скрипта т.к. все запросы делаются по очереди и общее время их выполнения будет равно сумме времен потраченных на каждый запрос в отдельности. Хорошо если запросов 5, а если их 100? Сами представляете сколько будет выполняться скрипт. Но выход есть! ![]()
Мы будем использовать библиотеку CURL, у которой есть возможность создавать мульти запросы.
Что нам это дает? Общее время выполнения всех запросов будет равно времени выполнения самого долгого запроса. Согласитесь это очень круто!
Вот собственно функция которая все это сделает:
< ?php
/**
* Функция мульти запроса на CURL
* @param array $data Данные для запроса
* @param array $options Опции для всех запросов
* @param array $oneoptions Опции для отдельных запросов
* @return array
*/
function multiRequest($data, $options = array(), $oneoptions = array())
{
// Массив для ресурсов соединения
$curls = array();
// Массив для результатов
$result = array();
// Инициализация мульти запроса
$mh = curl_multi_init();
// Задание параметров запроса
foreach ($data as $id => $d)
{
$curls[$id] = curl_init();
$url = (is_array($d) && !empty($d['url'])) ? $d['url'] : $d;
curl_setopt($curls[$id], CURLOPT_URL, $url);
curl_setopt($curls[$id], CURLOPT_HEADER, false);
curl_setopt($curls[$id], CURLOPT_RETURNTRANSFER, true);
// Дополнительные опции общие запросов
if (!empty($options))
{
curl_setopt_array($curls[$id], $options);
}
// Дополнительные опции для определенного запроса
if (!empty($oneoptions[$id]))
{
curl_setopt_array($curls[$id], $oneoptions[$id]);
}
// Если post запрос
if (is_array($d))
{
if (!empty($d['post']))
{
curl_setopt($curls[$id], CURLOPT_POST, 1);
curl_setopt($curls[$id], CURLOPT_POSTFIELDS, $d['post']);
}
}
curl_multi_add_handle($mh, $curls[$id]);
}
// Выполняем запрос пока есть соединения
$running = null;
do
{
curl_multi_exec($mh, $running);
}
while($running > 0);
// Получаем данные и закрываем соединения
foreach($curls as $id => $content)
{
$result[$id] = curl_multi_getcontent($content);
curl_multi_remove_handle($mh, $content);
}
curl_multi_close($mh);
return $result;
}
?>
Примеры использования.
1) Простой GET запрос с дополнительными параметрами:
$data = array(
'http://webiteam.ru/test/multicurl/test.php?data=This+is+get+first',
'http://webiteam.ru/test/multicurl/test.php?data=This+is+get+second',
'http://webiteam.ru/test/multicurl/test.php?data=This+is+get+third',
);
$options = array(
CURLOPT_REFERER => 'http://webiteam.ru/',
CURLOPT_TIMEOUT => 5
);
$oneoptions[0] = array(CURLOPT_USERAGENT => 'IE');
$r = multiRequest($data, $options,$oneoptions);
var_dump($r);
Результат:
array
0 => string 'This is get first
http://webiteam.ru/
IE
' (length=41)
1 => string 'This is get second
http://webiteam.ru/
' (length=39)
2 => string 'This is get third
http://webiteam.ru/
' (length=38)
2) POST запрос с дополнительными параметрами:
$data = array(array(),array());
$data[0]['url'] = 'http://webiteam.ru/test/multicurl/test.php';
$data[0]['post']['postdata'] = 'This is post first';
$data[1]['url'] = 'http://webiteam.ru/test/multicurl/test.php';
$data[1]['post']['postdata'] = 'This is post second';
$options = array(
CURLOPT_TIMEOUT => 10
);
$oneoptions[0] = array(CURLOPT_USERAGENT => 'Mozilla');
$oneoptions[1] = array(CURLOPT_USERAGENT => 'Opera');
$r = multiRequest($data, $options, $oneoptions);
var_dump($r);
Результат:
array
0 => string 'This is post first
Mozilla
' (length=27)
1 => string 'This is post second
Opera
' (length=26)
Как видите ничего сложного!
Список всех дополнительных опций для CURL тут.
Живой пример на котором можно оценить скорость.
Скачать пример
Скачал пример для тестирования скорости и не все оказалось так быстро как есть на самом деле:)
мультизапрос ( $r = multiRequest($data); ) нужно выполнять после запуска таймера, а не перед ним, иначе отображается не время запроса а время печати результатов:)
но в любом случае мультикурл быстрее:)
но я лично столкнулся с проблемкой, ломаю голову над тем как реализовать followlocation (точнее его заменитель) для мультикурла. нужно это для того чтобы не выскакивали ошибки если скрипт не в open_basedir ( CURLOPT_FOLLOWLOCATION cannot be activated when in safe_mode or an open_basedir )
реализация для обычных запросов есть, а вот для мульти как-то не соображу…
Для получения информации о времени запроса лучше использовать curl_info. А для обработки safe_mode или open_basedir заранее узнать через ini_get значение этих настроек.
А с таймером реально косяк вышел %) Спасибо!
Странно, но в живом примере последовательные запросы всегда быстрее, чем мультизапрос.
@Гость
Скорее всего это из-за того, что запросы идут на один и тот-же скрипт, и он начинает притормаживать с ответами =\
Отправил запросы на другой сервер. Теперь разница заметна =)
[...дополнительных опций для CURL тут.] – ссылка битая
@Данила
Спасибо, обновил. Отголоски удаленного русского мануала =(
при таком подходе скрипт будет вываливаться после несаких тысячь линков.
необходимо добавить
foreach($curls as $id => $content)
{
$result[$id] = curl_multi_getcontent($content);
curl_multi_remove_handle($mh, $content);
curl_close($content); !!!!!!!!!!!!!!!!!!!!!!!
}
@KaB
Если вы хотите несколько тысяч линков пустить в один поток, то в любом случаи вам потребуется много ресурсов.
А если обратите внимание на строчку ниже curl_multi_close($mh), то станет ясно, что вы ошибаетесь. Эта функция закроет все текущие потоки и освободит ресурсы.
незнаю незнаю. у меня ничего не вываливается)) и обрабатывается не пачками, когда один линк тормозит все, а помере прихода контента.
и еще в вашем варианте этот скрипт явно положит сервер, посмотрите на это
do
{
curl_multi_exec($mh, $running);
}
while($running > 0);
usleep() поставьте хоть что ли.))
Ну а по сути полезная инфа.))
Удачи)
как сделать, чтобы результат был без «string()»?
@Я
В каком смысле? Где «string()»?
Как сделать, чтобы данные после запроса не выводились?
Ступил, не знал о функции var_dump
Здравствуйте! А как посоветуете сделать, чтобы сначала можно было авторизоваться, а потом сразу же перейти на другую страницу этого же сайта? Авторизацию я прошёл, куки сохранил, а что дальше – не могу придумать.
TUX, воспользовался скриптом. Работает, спасибо. Есть вопрос, т.к. недостаточно знаний. Нельзя ли добавить опцию или условие так, что curl собирает не всю страницу, а лишь ее метаданные (title, keyword, description и т.д.)?
З.Ы. Так же интересует возможность вывода прогресс-бара для визуализации процесса работы скрипта. Т.е. выводить последовательно что-то типа «1 из 10..», «2 из 10″ и т.д. Я так понимаю, по умолчанию есть такая возможность, верно?
@Buzzman
Нет, забрать мета теги можно только вместе со всей страницей.
Не нашел подходящей темы для вопроса по cURL кроме этой.
Как через cURL послать запрос по форме, т.е. надо выбрать 1 из вариантов выпадающего списка, потом нажать на кнопку post, перейти на новую страницу и сохранить данные с новой страницы.
По идеи это всё эмуляция действий пользователя. А вот как сделать найти не могу. Может напишите по этому поводу статью?
Если данные в форме динамические, т.е. меняются от пользователя к пользователю, то нужно сначала зайти на страницу с формой, и собрать с нее данные (регуляркой или через DOM).
Когда нам известно содержимое формы мы можем отправить запрос к странице акшина и получить ее исходный код.
Если не понятно как отправить данные выпадающего списка то все просто:
у элемента select есть атрибут name, а у нужного значения(тег option) есть атрибут value…