В этой статье я расскажу об одной необычной проблеме, с которой мне однажды пришлось столкнуться по роду своей деятельности. Внимание: эта статья не для начинающих. Предполагается, что читатель уже имеет опыт веб-программирования и знаком с языком PHP, библиотекой CURL и основами HTTP.
Перейдем к описанию проблемы.
Мне нужно было написать скрипт бота для одного сайта с целью автоматизации некоторого процесса из нескольких шагов, не считая авторизации. Такова была задача, на первый взгляд, ничего особенного. Впрочем, поначалу так оно и было. Т.к. на сайте применялась авторизация и активно использовались cookies, было решено использовать CURL. Я понемногу сниффил HTTP-запросы к серверу сайта и воспроизводил их у себя в скрипте. Процесс, как говорится, шел…. Неприятности начались на предпоследнем шаге, когда сервер совершенно неожиданно для меня отказался выдавать желаемый результат. Это привело меня в состояние ступора, в котором я пребывал довольно долгое время, продолжая снова и снова сравнивать логи «искусственных» и «естественных» (браузерных) запросов в надежде найти хоть какое-то несовпадение. Прошло несколько часов, прежде чем я понял, что впустую трачу время.
И тогда я обратил свое внимание на так называемые JavaScript (или non-HTTP) cookies. Разумеется, CURL не мог отследить их появление и соответственно не мог добавить их в свои запросы. Но зачем серверу проверять JavaScript cookies? Хороший вопрос, в другой раз он бы обязательно меня заинтересовал, но в тот момент у меня были другие заботы.
Забегая вперед, скажу, что моя догадка оказалась верной. Сервер и в самом деле проверял JS куки, причем только на том злополучном предпоследнем шаге. Я не знаю, с какой целью это было сделано. Тем более я не уверен, что этой целью была защита от ботов. Как говорится, чужая душа (сервер) — потемки.
Итак, мне предстояло решить следующие две задачи:
Насчет первой задачи мне, в принципе, было все ясно, во всяком случае, я уже представлял себе примерный план действий. Так что я решил сразу начать со второй: найти средство для добавления «своих» куков в CURL-запрос, дополнительно к тем, что появляются там автоматически, из файла, указанного опцией CURLOPT_COOKIEFILE.
Первым, что пришло мне в голову, была мысль: включить опцию CURLOPT_COOKIE со строкой, составленной из параметров куков. Примерно так:
Так я и сделал: добавил эту строку в код… и очень скоро убедился, что это не работает. Вернее работает, но совсем не так как мне хотелось. В отправленном HTTP-заголовке были только куки, добавленные этой опцией, а вот куки из файла CURLOPT_COOKIEFILE при этом исчезли (исчезли из заголовка, а не из файла). Т.е. содержимое файла-хранилища куков игнорировалось. Из этого следует простойи бесполезный вывод: опции CURLOPT_COOKIE и CURLOPT_COOKIEFILE/CURLOPT_COOKIEJAR нельзя использовать вместе.
Короче, решить проблему по-быстрому не получилось. Поиск готовых решений в интернете тоже ничего не дал. Может быть я плохо искал, но все, что мне удалось найти по этой теме, вот этот вопрос, заданный на форуме StackOverflow, и как видно, оставшийся без ответа.
А тем временем срок сдачи работы подходил к концу, заказчик требовал объяснений, а у меня, как назло, куда-то пропало все мое красноречие. Тут мне почему-то вспомнилась крылатая фраза из одной советской комедии: «Либо я веду ее в загс, либо она ведет меня к прокурору». Ни в загс, ни к прокурору, мне не хотелось. Мне очень хотелось куда-нибудь скрыться и забыться… но пришлось выбирать другой способ устранения проблем.
К тому времени я уже видел два пути решения:
После недолгих колебаний был выбран второй вариант, т.к. он показался мне более легким в реализации. К тому же меня давно уже интересовал формат Netscape Cookie File, но не было повода познакомиться с ним поближе. И вот этот повод появился.
Искать информацию по этому формату долго не пришлось. С первой же страницы выдачи гугла я попал на оф. сайт CURL'а, в архив переписки пользователей с создателем этой библиотеки, где и нашел то, что искал.
Формат файла оказался довольно простым – 7 полей (атрибутов) в каждой строке, разделенных tab'ами и идущих в таком порядке:
Смысл этих полей имхо вполне очевиден. Отмечу только, что tailmatch – это флаг точного совпадения доменного имени сайта.
Теперь, когда формат файла был известен, остальное было уже делом техники.
В итоге мной был написан небольшой класс CookiejarEdit, исходный код которого привожу прямо здесь:
Методы __clone(), export(), import() были добавлены «чисто для украшения» кода. Честно говоря, я не вижу в них большого смысла, так же как и в дополнительном аргументе $aXtra для метода setPrefix, который я добавил просто на всякий случай (хотя имхо, необходимость в нем не может возникнуть по определению). В любом случае код рабочий и готов к использованию (PHP >= 5.0).
Я не претендую на оригинальность идеи и не исключаю возможности, что это очередной «велосипед». Возможно, аналогичные и даже более простые решения давно уже существуют. Тем не менее, мне мой «велосипед» помог и я буду рад, если он поможет кому-нибудь еще.
Итак, инструмент для «продвинутой» работы с куками был готов. Но на этом мои приключения не закончились. Предстояло еще отследить те самые «значимые» JS куки, понять какие значения им присваиваются и многое другое. Но это уже другая история. А эту я пожалуй закончу. Спасибо за внимание.
P.S.:
Перечитав текст статьи в очередной раз, я понял, что ей все-таки недостает «примера из реальной жизни». Неплохо было бы, подумал я, устроить небольшую демонстрацию на примере какого-нибудь сайта. Использовать тот сайт, с которым я работал, я, по определенным причинам, не мог. Поэтому нужно было найти ему замену: какой-нибудь известный сайт, не требующий авторизации, где хоть как-то используется проверка JavaScript cookies на стороне сервера. К моему удивлению и счастью такой сайт нашелся очень быстро: в моих закладках. Это всем известный Яндекс-Каталог, категория Фриланс (то, что мне ближе всего).
Сначала эта страница выглядит так:

Но если перейти к настройкам, выбрать там пункт «стандартное с номерами»:

и вернуться на страницу каталога, то мы добъемся «чудесного» эффекта: превью со страницы исчезнут и останутся только «сухие» цифры и текст:

Давайте попробуем написать простейший бот для скачивания первой страницы этого каталога без превьюшек. Я понимаю, как глупо это выглядит со стороны: писать бота только для того чтобы изменить вид страницы. Но не забывайте, что это всего лишь пример. Давайте представим, что получение страницы без превьюшек – наше самое заветное желание).
Для начала проведем рекогносцировку. Обратим внимание на url страницы каталога после изменения настроек: он не изменился. Правда, к нему добавилась строка "?rnd=xxx", но это, по всей видимости, всего лишь указание браузеру не брать страницу из кэша. Отсюда можно сделать вывод, что настройки передаются и сохраняются, скорее всего, через куки.
Попробуем разобраться как именно это происходит. В этом нам поможет такой полезный инструмент, как Live HTTP Headers:

Это расширение Firefox'а позволяет отслеживать весь входящий и исходящий HTTP-траффик в браузере, в том числе и куки, посредством которых и осуществляется запоминание настроек в нашем примере. Происходит это, очевидно, после нажатия кнопки «Сохранить» и перехода к странице каталога.
Зайдем еще раз на страницу настроек, предварительно включив снифер Live HTTP Headers. Выберем снова пункт «стандартное с номерами» и нажмем кнопку «Сохранить». А теперь посмотрим на наш улов в снифере. Нас интересуют подробности запроса страницы каталога. У меня они имеют такой вид:
Здесь сразу бросается в глаза фрагмент "yaca_view=num". Скорее всего, это и есть наш искомый cookie-параметр. Но где он устанавливается? Во всяком случае не в заголовках ответа сервера, поскольку там этот параметр не встречается. Тогда логично предположить, что это JavaScript cookie и значит, его установка происходит где-то в яваскриптах страницы настроек ("setup.xml"). Попробуем найти его в тексте этой страницы. Так и есть. Вот строка из файла "setup.xml":
По всей видимости здесь и происходит установка параметра "yaca_view" со значением, взятым из одноименного элемента формы (в нашем случае это значение 'num').
Итак, мы выяснили, что для того, чтобы увидеть страницу каталога без превьюшек, нужно передать серверу cookie-параметр с именем 'yaca_view' и значением 'num'. Теперь, когда у нас уже есть средство для добавления cookies в CURL-запросы, можно без особого труда написать скрипт бота. Вот его код с небольшими комментариями:
Теперь действительно все. Еще раз спасибо за внимание, особенно тем кто дочитал до конца).
Перейдем к описанию проблемы.
Мне нужно было написать скрипт бота для одного сайта с целью автоматизации некоторого процесса из нескольких шагов, не считая авторизации. Такова была задача, на первый взгляд, ничего особенного. Впрочем, поначалу так оно и было. Т.к. на сайте применялась авторизация и активно использовались cookies, было решено использовать CURL. Я понемногу сниффил HTTP-запросы к серверу сайта и воспроизводил их у себя в скрипте. Процесс, как говорится, шел…. Неприятности начались на предпоследнем шаге, когда сервер совершенно неожиданно для меня отказался выдавать желаемый результат. Это привело меня в состояние ступора, в котором я пребывал довольно долгое время, продолжая снова и снова сравнивать логи «искусственных» и «естественных» (браузерных) запросов в надежде найти хоть какое-то несовпадение. Прошло несколько часов, прежде чем я понял, что впустую трачу время.
И тогда я обратил свое внимание на так называемые JavaScript (или non-HTTP) cookies. Разумеется, CURL не мог отследить их появление и соответственно не мог добавить их в свои запросы. Но зачем серверу проверять JavaScript cookies? Хороший вопрос, в другой раз он бы обязательно меня заинтересовал, но в тот момент у меня были другие заботы.
Забегая вперед, скажу, что моя догадка оказалась верной. Сервер и в самом деле проверял JS куки, причем только на том злополучном предпоследнем шаге. Я не знаю, с какой целью это было сделано. Тем более я не уверен, что этой целью была защита от ботов. Как говорится, чужая душа (сервер) — потемки.
Итак, мне предстояло решить следующие две задачи:
- найти «значимые» JavaScript cookies, которые влияют на ответ сервера;
- найти способ вставить эти куки в запросы CURL.
Насчет первой задачи мне, в принципе, было все ясно, во всяком случае, я уже представлял себе примерный план действий. Так что я решил сразу начать со второй: найти средство для добавления «своих» куков в CURL-запрос, дополнительно к тем, что появляются там автоматически, из файла, указанного опцией CURLOPT_COOKIEFILE.
Первым, что пришло мне в голову, была мысль: включить опцию CURLOPT_COOKIE со строкой, составленной из параметров куков. Примерно так:
curl_setopt($hc, CURLOPT_COOKIE, "name1=value1; name2=value2; ...");
Так я и сделал: добавил эту строку в код… и очень скоро убедился, что это не работает. Вернее работает, но совсем не так как мне хотелось. В отправленном HTTP-заголовке были только куки, добавленные этой опцией, а вот куки из файла CURLOPT_COOKIEFILE при этом исчезли (исчезли из заголовка, а не из файла). Т.е. содержимое файла-хранилища куков игнорировалось. Из этого следует простой
Короче, решить проблему по-быстрому не получилось. Поиск готовых решений в интернете тоже ничего не дал. Может быть я плохо искал, но все, что мне удалось найти по этой теме, вот этот вопрос, заданный на форуме StackOverflow, и как видно, оставшийся без ответа.
А тем временем срок сдачи работы подходил к концу, заказчик требовал объяснений, а у меня, как назло, куда-то пропало все мое красноречие. Тут мне почему-то вспомнилась крылатая фраза из одной советской комедии: «Либо я веду ее в загс, либо она ведет меня к прокурору». Ни в загс, ни к прокурору, мне не хотелось. Мне очень хотелось куда-нибудь скрыться и забыться… но пришлось выбирать другой способ устранения проблем.
К тому времени я уже видел два пути решения:
- отказаться от услуг CURL'а в плане автоматической обработки cookies и взять эту «черную» работу себе, т.е. самому парсить куки из заголовков ответа, сохранять их, и передавать вместе с запросами. Звучит немного пугающе, но зато это дает полный контроль над куками.
- оставить авто обработку cookies, но добавить возможность вставки в файл куков «своих» (кастомных) параметров. Тоже перспектива не из приятных, поскольку это предполагало ручную правку файла куков.
После недолгих колебаний был выбран второй вариант, т.к. он показался мне более легким в реализации. К тому же меня давно уже интересовал формат Netscape Cookie File, но не было повода познакомиться с ним поближе. И вот этот повод появился.
Искать информацию по этому формату долго не пришлось. С первой же страницы выдачи гугла я попал на оф. сайт CURL'а, в архив переписки пользователей с создателем этой библиотеки, где и нашел то, что искал.
Формат файла оказался довольно простым – 7 полей (атрибутов) в каждой строке, разделенных tab'ами и идущих в таком порядке:
- domain
- tailmatch
- path
- secure
- expires
- name
- value
Смысл этих полей имхо вполне очевиден. Отмечу только, что tailmatch – это флаг точного совпадения доменного имени сайта.
Теперь, когда формат файла был известен, остальное было уже делом техники.
В итоге мной был написан небольшой класс CookiejarEdit, исходный код которого привожу прямо здесь:
<?php
class CookiejarEdit
{
protected $sFname= false; // имя файла cookies
protected $aPrefix= array( // массив значений общих полей записей
'', // #0: domain
'FALSE', // #1: tailmatch (строгое совпадение доменного имени)
'/', // #2: path
'FALSE', // #3: secure (https-соединение)
);
protected $sPrefix= ''; // строка значений общих полей записей cookie
function __construct($sFn, $sDomain='', $aXtra=0) {
if (!$sFn) return;
$this->sFname= $sFn;
$this->setPrefix($sDomain, $aXtra);
}
function __clone() {
$this->setPrefix();
}
/****
** Инициализация/установка общих полей записей cookie:
** Аргументы:
** 1) $sDomain - значение поля 'domain'
** 2) $aXtra - массив значений дополн-ных общих полей:
** $aXtra['tailmatch'] - значение поля 'tailmatch'
** $aXtra['path'] - значение поля 'path'
** $aXtra['secure'] - значение поля 'secure'
*/
function setPrefix($sDomain, $aXtra=0) {
if ($sDomain)
$this->aPrefix[0]= $sDomain;
if (is_array($aXtra)) {
if (isset($aXtra['tailmatch']))
$this->aPrefix[1]= $aXtra['tailmatch']? 'TRUE': 'FALSE';
if (isset($aXtra['path']))
$this->aPrefix[2]= $aXtra['path'];
if (isset($aXtra['secure']))
$this->aPrefix[3]= $aXtra['secure']? 'TRUE': 'FALSE';
}
if ($this->aPrefix[0])
$this->sPrefix= implode("\t", $this->aPrefix). "\t";
}
/****
** Экспорт содержимого файла cookies:
*/
function export() {
return ($this->sFname)? file_get_contents($this->sFname) : false;
}
/****
** Импорт содержимого файла cookies:
*/
function import($sCont) {
if (!$sCont || strlen($sCont)<10) return false;
file_put_contents($this->sFname, $sCont);
return true;
}
/****
** Добавление/изменение/удаление записи в/из файла cookies:
** Аргументы:
** 1) $aFields - массив значений индивидуальных полей записи cookie
** $aFields[0] - поле 'name' (имя параметра)
** $aFields[1] - поле 'value' (значение параметра)
** $aFields[2] - срок хранения записи в днях
** Возвращает значения:
** 1) false - в случае неправильного вызова
** 2) true - в случае успеха удаления
** 3) string - в случае успеха добавления/изменения, содержимое строки записи
*/
function setCookie($aFields) {
if (!$this->sFname || !$this->sPrefix)
return false;
if (!is_array($aFields) || !($n_arr= count($aFields)))
return false;
$name= $aFields[0];
$cont= file_exists($this->sFname)? file_get_contents($this->sFname): '';
$cr= (strpos($cont, "\r\n") !== false)? "\r\n" : "\n";
$a_rows= explode($cr, trim($cont, $cr));
$i_row= -1;
foreach ($a_rows as $i=> $row) {
if (strpos($row, "\t".$name."\t") === false) continue;
if (strpos($row, $this->sPrefix) !== 0) continue;
$i_row= $i; break;
}
$ret= true;
if ($n_arr> 1) {
// add/modify:
$val= $aFields[1];
$life= ($n_arr> 2 && $aFields[1]>= 0)? $aFields[1] : 1;
if ($i_row<0) $i_row= count($a_rows);
$n_exp= ($life> 0)? (time()+ $life* 24* 60* 60) : 0;
$a_rows[$i_row]= $ret=
$this->sPrefix. implode("\t", array($n_exp, $name, $val));
}
else if ($i_row>= 0) {
// remove:
unset($a_rows[$i_row]);
}
file_put_contents($this->sFname, implode($cr, $a_rows).$cr);
return $ret;
}
/****
** Добавление/изменение записи в файл cookies:
*/
function addCookie($sName, $sVal, $nLife=0) {
return $this->setCookie(array($sName, $sVal, $nLife));
}
/****
** Удаление записи из файла cookies:
*/
function removeCookie($sName) {
return $this->setCookie(array($sName));
}
}
?>
Методы __clone(), export(), import() были добавлены «чисто для украшения» кода. Честно говоря, я не вижу в них большого смысла, так же как и в дополнительном аргументе $aXtra для метода setPrefix, который я добавил просто на всякий случай (хотя имхо, необходимость в нем не может возникнуть по определению). В любом случае код рабочий и готов к использованию (PHP >= 5.0).
Я не претендую на оригинальность идеи и не исключаю возможности, что это очередной «велосипед». Возможно, аналогичные и даже более простые решения давно уже существуют. Тем не менее, мне мой «велосипед» помог и я буду рад, если он поможет кому-нибудь еще.
Итак, инструмент для «продвинутой» работы с куками был готов. Но на этом мои приключения не закончились. Предстояло еще отследить те самые «значимые» JS куки, понять какие значения им присваиваются и многое другое. Но это уже другая история. А эту я пожалуй закончу. Спасибо за внимание.
P.S.:
Перечитав текст статьи в очередной раз, я понял, что ей все-таки недостает «примера из реальной жизни». Неплохо было бы, подумал я, устроить небольшую демонстрацию на примере какого-нибудь сайта. Использовать тот сайт, с которым я работал, я, по определенным причинам, не мог. Поэтому нужно было найти ему замену: какой-нибудь известный сайт, не требующий авторизации, где хоть как-то используется проверка JavaScript cookies на стороне сервера. К моему удивлению и счастью такой сайт нашелся очень быстро: в моих закладках. Это всем известный Яндекс-Каталог, категория Фриланс (то, что мне ближе всего).
Сначала эта страница выглядит так:

Но если перейти к настройкам, выбрать там пункт «стандартное с номерами»:

и вернуться на страницу каталога, то мы добъемся «чудесного» эффекта: превью со страницы исчезнут и останутся только «сухие» цифры и текст:

Давайте попробуем написать простейший бот для скачивания первой страницы этого каталога без превьюшек. Я понимаю, как глупо это выглядит со стороны: писать бота только для того чтобы изменить вид страницы. Но не забывайте, что это всего лишь пример. Давайте представим, что получение страницы без превьюшек – наше самое заветное желание).
Для начала проведем рекогносцировку. Обратим внимание на url страницы каталога после изменения настроек: он не изменился. Правда, к нему добавилась строка "?rnd=xxx", но это, по всей видимости, всего лишь указание браузеру не брать страницу из кэша. Отсюда можно сделать вывод, что настройки передаются и сохраняются, скорее всего, через куки.
Попробуем разобраться как именно это происходит. В этом нам поможет такой полезный инструмент, как Live HTTP Headers:

Это расширение Firefox'а позволяет отслеживать весь входящий и исходящий HTTP-траффик в браузере, в том числе и куки, посредством которых и осуществляется запоминание настроек в нашем примере. Происходит это, очевидно, после нажатия кнопки «Сохранить» и перехода к странице каталога.
Зайдем еще раз на страницу настроек, предварительно включив снифер Live HTTP Headers. Выберем снова пункт «стандартное с номерами» и нажмем кнопку «Сохранить». А теперь посмотрим на наш улов в снифере. Нас интересуют подробности запроса страницы каталога. У меня они имеют такой вид:
GET /yca/cat/Employment/Freelance/?rnd=191 HTTP/1.1 Host: yaca.yandex.ru User-Agent: ... Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip,deflate Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7 Keep-Alive: 115 Connection: keep-alive Referer: http://yaca.yandex.ru/setup.xml Cookie: yandexuid=796954901281541279; fuid01=4c62c49f04c00e82.pQ2hPKLWAnitiiTOBnW-nvOhiFssICTMfcKaMv0ZeTsFKaxVHOYxAPA2AGRsdF1qi3rm7fAKk77gJevuaNmhtnNUx_k0ykECc8bRJv3dUadZ_YDF1QLDZddTzYP_ZfOs; my=YwA=; L=eEAcXVFJR252Q0ADVkt9BW5wWmFyXXhXBkBYAwQaYmIRBgo6Ciw9ZggRFwUmNQwcOUs5LwQvVD42OjAPCmFfFQ==.1310050733.9042.213864.a4f928b9d113358bc254454a879f6c5c; yp=1636215158.sp.; yabs-frequency=/3/UOW2AQmAGyle0Ici2au0/; yaca_view=num
Здесь сразу бросается в глаза фрагмент "yaca_view=num". Скорее всего, это и есть наш искомый cookie-параметр. Но где он устанавливается? Во всяком случае не в заголовках ответа сервера, поскольку там этот параметр не встречается. Тогда логично предположить, что это JavaScript cookie и значит, его установка происходит где-то в яваскриптах страницы настроек ("setup.xml"). Попробуем найти его в тексте этой страницы. Так и есть. Вот строка из файла "setup.xml":
$.cookie('yaca_view', $('input[name="yaca_view"]:checked' ).val());
По всей видимости здесь и происходит установка параметра "yaca_view" со значением, взятым из одноименного элемента формы (в нашем случае это значение 'num').
Итак, мы выяснили, что для того, чтобы увидеть страницу каталога без превьюшек, нужно передать серверу cookie-параметр с именем 'yaca_view' и значением 'num'. Теперь, когда у нас уже есть средство для добавления cookies в CURL-запросы, можно без особого труда написать скрипт бота. Вот его код с небольшими комментариями:
<?php
require_once "cookiejaredit.inc";
if (!function_exists('curl_setopt_array')) {
function curl_setopt_array(&$hc, $a_opts) {
foreach ($a_opts as $name=> $val)
if (!curl_setopt($hc, $name, $val)) return false;
return true;
}
}
/****
** Скачивание файла с помощью CURL:
** Аргументы:
** 1) $aOpts - массив значений опций CURL:
** 2) $sUrl - URL файла
** 3) $sUrlRef - URL реферера
** Возвращает значения:
** 1) false - в случае ошибки
** 2) string - в случае успеха, содержимое файла
*/
function getByCurl($aOpts, $sUrl, $sUrlRef='') {
$hc= curl_init();
curl_setopt_array($hc, $aOpts);
curl_setopt($hc, CURLOPT_URL, $sUrl);
curl_setopt($hc, CURLOPT_REFERER, $sUrlRef);
$cont= curl_exec($hc);
$b_ok= curl_errno($hc)==0 && curl_getinfo($hc, CURLINFO_HTTP_CODE)==200;
echo "\nSent HTTP Header:\n". curl_getinfo($hc, CURLINFO_HEADER_OUT).
"Content Length: ".strlen($cont)."\n\n";
curl_close($hc);
return $b_ok? $cont : false;
}
// Имя (путь) файла cookies:
$fn_cook= $_SERVER['DOCUMENT_ROOT'].'/cookiejar-tmp.txt';
// Массив значений опций CURL:
$a_curl_opts= array(
CURLOPT_NOBODY => 0,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 15,
CURLOPT_USERAGENT => 'Mozilla/5.0 Gecko/20110920 Firefox/3.6.23',
CURLINFO_HEADER_OUT => true,
CURLOPT_COOKIEFILE => $fn_cook,
CURLOPT_COOKIEJAR => $fn_cook,
);
define('URL0', 'http://yaca.yandex.ru/yca/cat/Employment/Freelance/');
define('URL1', 'http://yaca.yandex.ru/setup.xml');
define('URL2', 'http://yaca.yandex.ru/yca/cat/Employment/Freelance/?rnd=');
define('FN_RESULT', 'result.htm');
echo '<h3>Trace Log:</h3><pre>';
$cookedit= new CookiejarEdit($fn_cook, 'yaca.yandex.ru');
// Скачиваем страницу настроек:
getByCurl($a_curl_opts, URL1, URL0);
// Добавляем JS cookie-параметр:
$rec= $cookedit->addCookie('yaca_view', 'num');
echo "addCookie:\n". ($rec? "$rec\n" : "Fail\n");
// Скачиваем страницу каталога:
$cont= getByCurl($a_curl_opts, URL2. rand(0,999), URL1);
echo '</pre>
<hr><h3>Result: ';
if ($cont) {
file_put_contents(FN_RESULT, $cont);
echo 'OK</h3><a href="'.FN_RESULT.'" target="_blank">Result page</a>';
}
else
echo 'Fail</h3>';
?>
Теперь действительно все. Еще раз спасибо за внимание, особенно тем кто дочитал до конца).