Главная > PHP > Мульти HTTP-запрос на CURL

Мульти HTTP-запрос на CURL

Иногда возникает необходимость во время исполнения скрипта сделать несколько http запросов. При большом их количестве возникает проблема со временем обработки скрипта т.к. все запросы делаются по очереди и общее время их выполнения будет равно сумме времен потраченных на каждый запрос в отдельности. Хорошо если запросов 5, а если их 100? Сами представляете сколько будет выполняться скрипт. Но выход есть! ;-)

Мы будем использовать библиотеку CURL, у которой есть возможность создавать мульти запросы.
Что нам это дает? Общее время выполнения всех запросов будет равно времени выполнения самого долгого запроса. Согласитесь это очень круто! 8-)

Вот собственно функция которая все это сделает:

< ?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 тут.

Живой пример на котором можно оценить скорость.
Скачать пример

Categories: PHP Tags: ,
  1. Shad0w
    18 Октябрь 2009 в 23:58 | #1

    Скачал пример для тестирования скорости и не все оказалось так быстро как есть на самом деле:)
    мультизапрос ( $r = multiRequest($data); ) нужно выполнять после запуска таймера, а не перед ним, иначе отображается не время запроса а время печати результатов:)
    но в любом случае мультикурл быстрее:)

    но я лично столкнулся с проблемкой, ломаю голову над тем как реализовать followlocation (точнее его заменитель) для мультикурла. нужно это для того чтобы не выскакивали ошибки если скрипт не в open_basedir ( CURLOPT_FOLLOWLOCATION cannot be activated when in safe_mode or an open_basedir )

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

  2. 19 Октябрь 2009 в 05:30 | #2

    Для получения информации о времени запроса лучше использовать curl_info. А для обработки safe_mode или open_basedir заранее узнать через ini_get значение этих настроек.
    А с таймером реально косяк вышел %) Спасибо!

  3. Гость
    20 Ноябрь 2009 в 07:42 | #3

    Странно, но в живом примере последовательные запросы всегда быстрее, чем мультизапрос.

  4. 20 Ноябрь 2009 в 07:54 | #4

    @Гость
    Скорее всего это из-за того, что запросы идут на один и тот-же скрипт, и он начинает притормаживать с ответами =\

  5. 20 Ноябрь 2009 в 08:02 | #5

    Отправил запросы на другой сервер. Теперь разница заметна =)

  6. 25 Ноябрь 2009 в 13:36 | #6

    [...дополнительных опций для CURL тут.] – ссылка битая

  7. 25 Ноябрь 2009 в 13:46 | #7

    @Данила
    Спасибо, обновил. Отголоски удаленного русского мануала =(

  8. KaB
    10 Декабрь 2009 в 12:21 | #8

    при таком подходе скрипт будет вываливаться после несаких тысячь линков.
    необходимо добавить
    foreach($curls as $id => $content)
    {
    $result[$id] = curl_multi_getcontent($content);
    curl_multi_remove_handle($mh, $content);
    curl_close($content); !!!!!!!!!!!!!!!!!!!!!!!
    }

  9. 11 Декабрь 2009 в 07:25 | #9

    @KaB
    Если вы хотите несколько тысяч линков пустить в один поток, то в любом случаи вам потребуется много ресурсов.
    А если обратите внимание на строчку ниже curl_multi_close($mh), то станет ясно, что вы ошибаетесь. Эта функция закроет все текущие потоки и освободит ресурсы.

  10. KaB
    13 Декабрь 2009 в 07:07 | #10

    незнаю незнаю. у меня ничего не вываливается)) и обрабатывается не пачками, когда один линк тормозит все, а помере прихода контента.

    и еще в вашем варианте этот скрипт явно положит сервер, посмотрите на это
    do
    {
    curl_multi_exec($mh, $running);
    }
    while($running > 0);

    usleep() поставьте хоть что ли.))
    Ну а по сути полезная инфа.))
    Удачи)

  11. Я
    15 Март 2010 в 12:25 | #11

    как сделать, чтобы результат был без «string()»?

  12. 15 Март 2010 в 12:30 | #12


    В каком смысле? Где «string()»?

  13. Mister
    15 Март 2010 в 16:19 | #13

    Как сделать, чтобы данные после запроса не выводились?

  14. Mister
    15 Март 2010 в 16:29 | #14

    Ступил, не знал о функции var_dump

  15. Andrey
    14 Август 2010 в 16:24 | #15

    Здравствуйте! А как посоветуете сделать, чтобы сначала можно было авторизоваться, а потом сразу же перейти на другую страницу этого же сайта? Авторизацию я прошёл, куки сохранил, а что дальше – не могу придумать.

  16. 13 Ноябрь 2010 в 09:37 | #16

    TUX, воспользовался скриптом. Работает, спасибо. Есть вопрос, т.к. недостаточно знаний. Нельзя ли добавить опцию или условие так, что curl собирает не всю страницу, а лишь ее метаданные (title, keyword, description и т.д.)?

  17. 13 Ноябрь 2010 в 09:40 | #17

    З.Ы. Так же интересует возможность вывода прогресс-бара для визуализации процесса работы скрипта. Т.е. выводить последовательно что-то типа «1 из 10..», «2 из 10″ и т.д. Я так понимаю, по умолчанию есть такая возможность, верно?

  18. 13 Ноябрь 2010 в 09:49 | #18

    @Buzzman
    Нет, забрать мета теги можно только вместе со всей страницей.

  19. 16 Август 2011 в 17:57 | #19

    Не нашел подходящей темы для вопроса по cURL кроме этой.
    Как через cURL послать запрос по форме, т.е. надо выбрать 1 из вариантов выпадающего списка, потом нажать на кнопку post, перейти на новую страницу и сохранить данные с новой страницы.

    По идеи это всё эмуляция действий пользователя. А вот как сделать найти не могу. Может напишите по этому поводу статью?

  20. 16 Август 2011 в 21:46 | #20

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

    Если не понятно как отправить данные выпадающего списка то все просто:
    у элемента select есть атрибут name, а у нужного значения(тег option) есть атрибут value…

  1. Пока что нет уведомлений.