Поиск по блогу

понедельник, 14 сентября 2009 г.

CURL. First steps

Как правило, изучение нового инструмента всегда откладывается: "Потом. Еще успеется. Надо собраться с мыслями и все такое". Именно так у меня когда-то откладывалось начало работы с библиотекой CURL (Client URL). Эта статья будет для новичков. Главная ее цель — показать, что ничего страшного здесь нет. И можно получить базовые представления за те 10 минут, которые уйдут на чтение статьи. Более того — вы сразу можете опробовать новые навыки в деле, используя листинги простейших функций, которые я буду приводить по мере раскрытия темы.

Прикинув приблизительный объем материала, я поняла, что в одну статью все не влезет, так что продолжение будет следовать :)

Для того, чтобы начать работать с библиотекой CURL, ее надо установить. Инструкций установки CURL в сети найти можно много, поэтому останавливаться на этом не буду.

Перейдем сразу к работе. Разберем простой пример использования библиотеки CURL для получения кода страницы. Этот пример вы можете часто встретить в листингах скриптов (я взяла его с какого-то форума).

function get_web_page( $url )
{
$uagent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8";

$ch = curl_init( $url );

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // возвращает веб-страницу
curl_setopt($ch, CURLOPT_HEADER, 0); // не возвращает заголовки
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // переходит по редиректам
curl_setopt($ch, CURLOPT_ENCODING, ""); // обрабатывает все кодировки
curl_setopt($ch, CURLOPT_USERAGENT, $uagent); // useragent
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120); // таймаут соединения
curl_setopt($ch, CURLOPT_TIMEOUT, 120); // таймаут ответа
curl_setopt($ch, CURLOPT_MAXREDIRS, 10); // останавливаться после 10-ого редиректа

$content = curl_exec( $ch );
$err = curl_errno( $ch );
$errmsg = curl_error( $ch );
$header = curl_getinfo( $ch );
curl_close( $ch );

$header['errno'] = $err;
$header['errmsg'] = $errmsg;
$header['content'] = $content;
return $header;
}


В эту функцию в качестве параметра передается URL, а возвращается ассоциативный массив, состоящий из заголовка и содержимого страницы. Функции CURL автоматически управляют редиректами, кукисами, декомпрессией файлов.

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


Параметры заголовка можно посмотреть в мануалах-описаниях функции curl_getinfo().

Как видим, параметров там немало:

- "url"
- "content_type"
- "http_code"
- "header_size"
- "request_size"
- "filetime"
- "ssl_verify_result"
- "redirect_count"
- "total_time"
- "namelookup_time"
- "connect_time"
- "pretransfer_time"
- "size_upload"
- "size_download"
- "speed_download"
- "speed_upload"
- "download_content_length"
- "upload_content_length"
- "starttransfer_time"
- "redirect_time"


Наиболее полезными для нас будут:
url — адрес после всех редиректов;
content_type — "тип" контента (например, "text/html; charset=utf-8");
http_code — код ответа (если все прошло благополучно — 200. О кодах я уже писала на этом блоге раньше, в статье о HTTP-протоколе).

Дополнительными возвращаемыми параметрами приведенной выше функции будут:
errno — код ошибки CURL (если без ошибок — вернется 0);
errmsg — сообщение об ошибке (определяется по коду ошибки errno);
content — содержимое страницы (HTML-код, картинка, т.д.).

Когда все прошло удачно, errno = 0, http_code = 200 и в переменной content находится содержимое страницы.

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

Если возникает ошибка, связанная с отсутствием страницы (404) или с недостаточными правами, errno = 0, http_code — статус-код, отличный от 200, а в переменной content будет страница сайта, сообщающая об ошибке.

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

Пример ее использования:

$result = get_web_page( $url );

if ( $result['errno'] != 0 )
... ошибка: неправильный url, таймаут, зацикливание ...

if ( $result['http_code'] != 200 )
... ошибка: нет страницы, нет прав ...

$page = $result['content'];



Но разберемся с функциями в листинге по порядку. Чтобы получить web-страницу с помощью библиотеки CURL, необходимо действовать в соответствии со следующей инструкцией:

1. Инициализируем работу CURL с помощью curl_init(). При использовании этой функции создается объект для управления запросом. Единственным аргументом функции является URL, который сохраняется до тех пор, пока curl_exec() не выполнит запрос. В приведенном в начале статьи примере в листинге мы видели строку:

$ch = curl_init( $url );


После ее выполнения переменной $ch будет присвоен дескриптор объекта CURL для работы с адресом $url.

Есть еще немного другой способ выполнить то же самое.

$ch = curl_init( $url );
curl_setopt($ch, CURLOPT_URL, $url);


Как видно, адрес не передается в виде параметра, а указывается уже после инициализации с помощью curl_setopt. Curl_setopt — это как раз следующий шаг, рассмотрим его подробнее.


2. С помощью curl_setopt() настраиваем параметры запроса и их значения. Параметров на самом деле немало. Все их помнить необязательно, но ознакомиться стоит (http://ru.php.net/manual/en/function.curl-setopt.php).

Наиболее важными считаются:

CURLOPT_RETURNTRANSFER
Когда он равен true (или 1), CURL возвращает содержимое страницы с помощью curl_exec(). Когда значение этого параметра равно false, CURL выводит содержимое страницы на экран. Для парсинга нам всегда придется работать с CURLOPT_RETURNTRANSFER = 1, так как всегда требуется дальнейшая обработка содержимого.

CURLOPT_HEADER
Когда этот параметр установлен в true (или 1), CURL включает в страницу заголовки ответа HTTP-сервера. Когда false — они не включаются, что делает результат более "удобным" для дальнейшего разбора. Причем доступ к заголовкам при CURLOPT_HEADER=false получить можно, через упоминавшуюся выше функцию curl_getinfo().


В разряд "основных" входят также следующие параметры:

- CURLOPT_FOLLOWLOCATION
Когда параметр установлен в true, редирект осуществляется CURL-ом автоматически. Когда false — после первой же попытки редиректа происходит остановка работы и возвращается ошибка.

- CURLOPT_ENCODING
Когда данный параметр пустой, CURL управляет декомпрессией и компрессией. "Сжатые" страницы автоматически распаковываются перед тем, как данные возвращаются с помощью curl_exec().


Параметры, которые еще следует указывать, чтобы серфинг по вебу с использованием CURL был "наиболее грамотным":

- CURLOPT_USERAGENT
Ну, это понятно. User agent — идентифицирует приложение, с помощью которого выполнен запрос (например, веббраузер). Если этот параметр оставить пустым, некоторые веб-ресурсы могут не ответить на запрос.

- CURLOPT_AUTOREFERER
Реферер — URL страницы, с которой пришли с запросом. Когда разрешены редиректы, то достаточно установить этот параметр равным true (или 1) — и CURL будет автоматически заполнять рефереры при редиректе.


Параметры для выявления ошибок при выполнении запросов:

- CURLOPT_CONNECTTIMEOUT — время в секундах, через которое CURL перестанет пытаться соединиться с сервером, который не отвечает;

- CURLOPT_TIMEOUT — время в секундах, через которое CURL перестанет пытаться получить содержимое страницы с сервера, который не отвечает/не доступен/и так далее;

- CURLOPT_MAXREDIRS — устанавливает максимальное количество возможных редиректов.


Следует так же написать здесь о том, что в версиях PHP, начиная с 5.1.3, появилась функция curl_setopt_array(), которая позволяет установить все параметры разом, загнав их в массив.

$options = array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_ENCODING => "",
CURLOPT_USERAGENT => $uagent,
CURLOPT_AUTOREFERER => true,
CURLOPT_CONNECTTIMEOUT => 120,
CURLOPT_TIMEOUT => 120,
CURLOPT_MAXREDIRS => 10,
);

curl_setopt_array( $ch, $options );



3. После установки всех нужных параметров используем curl_exec(). curl_exec() отправляет запрос и возвращает веб-страницу (HTML, XML, image и т.д.).


4. curl_errno() и curl_error() мы уже рассмотрели в начале статьи.
Еще раз, в качестве резюме:

- Системные ошибки возникают из-за неверного url-а, протокола, неправильного хоста, таймаутов (соединения и ответа) и зацикливания. При таких ошибказ curl_errno() возвращает отличный от нуля код (libcurl errors) и curl_error() возвращает сообщение, описывающее ошибку.

- Ошибки сайта возникают, если страница не найдена или нет доступа. В этом случае curl_errno() вернет ноль, а код-статус страницы можно уточнить через описанный выше curl_getinfo().


5. curl_close() уничтожает объект, очищает память. curl_close() надо вызывать по окончанию работы с объектом CURL.

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

Продолжаю благодарить людей, оказавших информационную поддержку RSSAdder-у. На этот раз это автор блога о мини-бизнесе и не только :)
___

Чтобы быть в курсе обновлений блога, можно подписаться на RSS.

Статьи схожей тематики:



20 комментариев:

  1. Кстати начинающим можно присмотреться к библиотеке Snoopy - достаточно хорошая обертка над curl. Много удобных возможностей.

    ОтветитьУдалить
  2. А что значит "curl_setopt($ch, CURLOPT_HEADER, 0);// не возвращает заголовки"". Что за заголовки?

    ОтветитьУдалить
  3. Kubig, http://parsing-and-i.blogspot.com/2009/04/http.html :))

    ОтветитьУдалить
  4. Kubig, и вот здесь про протокол http можно почитать: http://parsing-and-i.blogspot.com/2009/04/http-protocol-http-requests.html. А потом уже спрашивай, если что останется непонятным.

    ОтветитьУдалить
  5. А каким образом получить в переменную адрес хоста после всех редиректов!?

    ОтветитьУдалить
  6. Очень бы интересно было посмотреть на пример автогенератора фрихост блогов на curl

    ОтветитьУдалить
  7. Здравствуйте! Может вы подскажете, столкнулся с такой проблемой. Выполняется скрипт авторизации с последующим заполнением некоторых форм, но после первого курл запроса, адрес страницы скрипта замещаеться адресом проблемного сайта, также это происходит независимо от того стоит FOLLOWLOCATION в 0 или 1.
    И еще такой вопрос. Нужно инициализировать сессию curl_init один раз, выполнять N запросов и закрывать или каждый раз перед запросом, или же разницы нету. Спасибо за ответы!))

    ОтветитьУдалить
  8. Жаль, что так и нет ответа на вопрос анонима. Можно ли между вызовами curl_init/curl_close делать несколько запросов curl_exec с разными curl_setopt?

    ОтветитьУдалить
  9. Ivan, я просто не понимаю, в чем особая проблема? Написать лишние 2 строчки? Лично я всегда делаю один curl_exec между curl_init/curl_close и считаю, что так логически и структурно правильно. Никогда даже не интересовалась, что будет, если использовать несколько вызовов :) Может, скилл любознательности у меня не прокачан...

    ОтветитьУдалить
  10. вопрос, а как проверить через curl степень анонимности прокси?

    ОтветитьУдалить
  11. Masha, меня этот вопрос заинтересовал с точки зрения производительности. В принципе не люблю выполнять какие-либо лишние вызовы функций, если есть шанс обойтись без них.

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

    ОтветитьУдалить
  12. Ответ Анонимному об анонимности прокси:

    Написать скрипт, который обращается через искомый прокси к какому-нибудь прокси-чекеру (первое, что нашел в гугле - http://aruljohn.com/details.php).

    Далее распарсить выданную прокси-чекером страницу и проанализировать информацию о прокси и айпи.

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

    ОтветитьУдалить
  13. Ivan, а как проверить если скрипт запускаю с локального компа? (это по второму варианту)

    вывел весь print_r($_SERVER); но что-то не пойму куда смотреть

    ОтветитьУдалить
  14. Смотреть нужно сюда:
    $_SERVER['REMOTE_ADDR'],
    $_SERVER['HTTP_X_FORWARDED_FOR'] или $_SERVER['X_FORWARDED_FOR'] или
    $_SERVER['HTTP_CLIENT_IP']

    Среди этих значений (если они вообще присутствуют) не должно быть вашего ip.

    ОтветитьУдалить
  15. Очень полезная статья а главное понятная. Но у меня возникла проблема:
    делаю парсинг как Вы описали на сервере, который выходит в интернет напрямую - все работает. А вот пытаюсь сделать парсинг с сервера, который спрятан за Proxy - ничего не получается пишет ошибку 56 - Failure when receiving data from the peer. Не пойму как это исправить. Подскажите что можно проверить.

    ОтветитьУдалить
  16. curl_setopt( $co, CURLOPT_FOLLOWLOCATION, 1);

    у вас написано что этот параметр нужен чтоб переходить по редиректам. Запустил ваш пример на сайте www.zhuoyush.com, чтоб просто сграбить главную страницу, редирект не схавало

    ОтветитьУдалить
  17. Слава, посмотрите внимательно код страницы ресурса. Может, там редирект устроен по-особомую

    ОтветитьУдалить
  18. Извините за запоздалую реакцию, но хотел бы внести замечание по поводу Snoopy, которого рекомендуют в первом комментарии.

    Не советую. Во-первых, при неудачном соединении, генерирует не только внутреннюю ошибку, но и вываливает два варнинга прямо в вывод.

    Во-вторых старый и не развивается. Устаревший код с большим количеством граблей.

    В-третьих он curl пытается через exec вызывать (!!!) и, если не получается, то пробует всё сделать через сокеты.

    В общем, даже не стоит по ссылке переходить. Спасибо.

    ОтветитьУдалить
  19. Анонимный8 июля 2011 г., 0:21

    Подскажите пожалуйста (я реализовал по вашему примеру функцию которая вызывается в цикле) у меня к примеру есть 150 ссылок и мне надо зайти по каждой из них и вытянуть какую нибудь информацию, но в цикле почему то просматриваются 5-15 ссылок и все!!! а как же остальные??

    ОтветитьУдалить
  20. Анонимный, откуда мне знать? :)) Я не вижу вашего кода, пальцем в небо тыкать бесполезно :)

    ОтветитьУдалить

Комментарии модерируются, вопросы не по теме удаляются, троллинг тоже.

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

Поделиться