Архивы для категории ‘Кодинг’.

Экономим трафик. Увеличиваем скорость.

Во многих проектах по парсингу (обработке) большого количества страниц очень выгодно использовать сжатие страниц на стороне сервера-источника. Это помогает экономить трафик со всеми истекающими плюсами.

Как проверить может ли сервер-источник сжимать контент?

Методов на самом деле очень много. Я же пользуюсь связкой Mozilla + Live HTTP Headers.
Для того, чтобы проверить может ли сервер-источник сжимать страницу, я запускаю Mozilla, потом Live HTTP Headers и ищу в ответе на запрос броузера поле Content-Encoding. Конечно же для более глубокого понимания всего процесса взаимодействия было бы неплохо знать спецификацию HTTP 1.1, но в и без нее можно обойтись.

Типичный ответ сервера-источника может выглядеть так:

Live HTTP Headers. Перехват

Как реализовать обработку «сжатых» страниц в своем PHP скрипте?

Для этого вам надо в своем коде сначала отправлять дополнительный заголовок. В своих скриптах я делаю это так:

$spider->AddHeaders[]=’Accept-Encoding: gzip’; // Как можно видеть на картинке выше, точно такое же поле отправляет и наш броузер
$spider->GetContent();

А внутри класса обрабатываем это дополнительное поле таким нехитрым кодом:
curl_setopt($this->curl_handler, CURLOPT_HEADER, $this->Headers);

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

$spider->Result=gzinflate(substr($spider->Result,10));

После проведения этой операции в $spider->Result будет уже удобоваримый контент, который можно обрабатывать.

У вас также могут возникнуть вопросы по поводу числа 10 в функции substr. Скажу что выбрано оно не случайно :). Это связано с тем что моды Апача, которые занимаются сжатием, неправильно отдают заголовки сжатого контента. Мы же их обрезаем и просто «расжимаем» контент. Скаже сразу, что этот код будет работать в 90% процентах случаев. Если же у вас он по какой-то причине не работает, то советую почитать комменты к функции gzinflate на сайте php.net. Думаю там вы найдете ответы на все ваши вопросы.

Что мы получаем в результате использования данного подхода?

На самом деле получаем очень много. Мы экономим трафик и увеличиваем скорость обработки, что крайне важно на больших проектах, особенно если источник находится за много прыжков от нас (посмотреть это можно при помощи команды tracert в консольном режиме Windows).

По поводу скорости я провел пару исследований. Средняя скорость загрузки (10 последовательных тестов) со сжатием составила примерно 0.5 секунды, без сжатия - 0.85 секунды. Сайт-источник находился от меня на расстоянии 10 прыжков (а если говорить правильнее, 10 промежуточных точек или 10 маршрутизаторов). В вышеуказанное время уже включены накладные расходы на “расжатие” страниц для первого теста и на удаление всех вайтспейсес ([\n\r\t]) для обоих случаев.

Как мы видим выигрыш получился равным 70%!!! Поэтому советую для крупных проектов взять эту технику себе на вооружение.

Многопоточный парсер ТИЦ на Perl

В качестве затравки своих Perl навыков предлагаю общественности на рассмотрение Перловый парсер значений ТИЦ.
Код я постарался снабдить большим количеством комментариев, так что разобраться в нем для вас не должно составить проблем.
Также хочу отметить, что данный код я написал не для практического применения, а исключительно в качестве примера многопоточности и ее реализации в Perl.

#!/usr/bin/perl -w
 
# Многопоточный парсер значения ТИЦ
# by ParserPro (http://parse.com.ua | http://parserpro.com)
# 12/01/2008
 
use strict;
use threads;
use threads::shared;
use LWP::Simple;
 
use constant BASE_URL => 'http://bar-navig.yandex.ru/u?ver=2&show=32&url=';   # базовый адрес откуда будем получать значение ТИЦа
use constant MAX_THREADS => 20;                                               # максимальное количество потоков. Можете поставить и больше ;)
 
my $thread_count : shared;                                                    # счетчик потоков
$thread_count=1;
 
# По умолчанию скрипт принимает 2 аргумента:
# - файл с урлами
# - имя файла, в который будут записаны значения ТИЦа
if(@ARGV != 2) {
    die "Usage: tyc.pl <urls_file> <output_file>\n";
}
 
open(URLS,'<'.$ARGV[0]) or die "Не могу открыть файл: $! \n";
my @lines;                  # массив для урлов
while (<URLS>) {
    chop;                   # убрали переводы строки    
    s/^\s+//;               # убрали предшествующие пробелм
    s/\s+$//;               # убрали постшествующие пробелы
    next unless length;     # если после этого длина = 0 перейти к следующей строке
    push @lines, $_;        # запихиваем урлы в большой массив
}
 
open(OUTPUT,'>>'.$ARGV[1]) or die "Не могу создать файл: $!\n";
my @proc;       # массив потоков
my $s = times;  # секундомер
foreach (@lines) {
    while ($thread_count > MAX_THREADS) {}      # пока количество потоков больше MAX_THREADS ждем
    push @proc, threads->create(\&getTYC,$_);   # запускаем поток
    $proc[$#proc]->join();                      # приатачиваем последний запущенный поток к главной программе
}
$s=(times-$s)*60;                               
print "Elapsed time: $s seconds\n";             
 
sub getTYC($) {
    ++$thread_count;     # запустили поток
    my $tycUrl = shift;
    my $url = BASE_URL.$tycUrl;
    my $content = get $url;
    if(defined $content) {
        if ($content =~ /value\=\"(.*?)\"/) {
            if ($1 eq '') {
                print "$tycUrl;!\n";
                print OUTPUT "$tycUrl;!\n";
            } else {
                print "$tycUrl;$1\n";
                print OUTPUT "$tycUrl;$1\n";
            }            
        } else {
            print "$tycUrl;!!\n";
            print OUTPUT "$tycUrl;!!\n";
        }
    } else {
        print "$tycUrl;!!!\n";
        print OUTPUT "$tycUrl;!!!\n";        
    }
    --$thread_count;    # завершили поток
}

У данного кода есть несколько преимуществ:

  • многопоточность
  • простота

и ряд недостатков:

  • использование очень медленной LWP (и как ее подвида LWP::Simple)
  • невозможность работы через прокси (добавить это легко, но мне было лень :) )
  • никакого контроля за адресами и их валидностью - тупо, что получили, то Яндексу и кормим (это добавить еще легче, но мне было еще больше в лень)
  • использование “дурного” метода обработки завершения потока.
    Я хотел использовать, что-то другое, но для него или не подходил мой Перл или работало оно явно не очень хорошо (как в случае с Threads::Queue).

Также скрипт не показал себя сильно производительным. На моем компе проверку 100 URL он произвел за 220 секунду, то есть по 2 секунды на адрес - ОЧЕНЬ МЕДЛЕННО! Также удивил тот факт, что такой примитивный скрипт отгрыз около 5 метров оперативной памяти (наверное ты это имел ввиду, SHAman, когда мы с тобой говорили про потоки). Притом при создании нового потока объем используемой памяти увеличивался. Со временем память высвобождалась. Но само поведение меня потоков меня напрягало.

Так что пробуйте скрипт у себя. Говорите о результатах, делитесь опытом, так сказать. А для себя я еще раз убедился, что PERL POE будет оптимальным решением для моих задач!

Удачи!

Где я?

Очень часто в процессе парсинга информации наши скрипты циклически проходят набор каких-либо урлов.

Если парсинг идет с одного источника с изменением, например, некоторых GET или POST параметров, то в любую единицу времени вы можете точно определить месторасположение своего скрипта. Но бывают случаи когда массив урлов собирается, например, с поисковика. И все было бы хорошо, если бы человек не придумал такую штуку как редирект и если бы ваш заказчик не требовал заносить УРЛ конечной страницы в базу данных.

А можете ли вы однозначно утверждать, что урл, который вы достали из выдачи Гугла однозначно равен урлу, на который зашел ваш скрипт и на котором он производил поиск информации? Если вы говорите “ДА”, то вы сильно ошибаетесь. Можете попробовать поэкспериментировать с такими запросами к Google как “Viagra” - половина сайтов будут либо дорвеями, либо прочими видами редиректеров.

И, поверьте, это всего лишь одна из ситуаций, когда в процессе парсинга/граббинга надо точно определять урл страницы, на которой находится ваш скрипт.

В качестве других типичных примеров можно привести сборы урлов из каталогов компаниий или каталогов галерей картинок. Как часто вы видите подобные урлы: http://www.site.com/redirect.php?id=700? Понятное дело, что подобное записывать в поле URL в своей базе просто стыдно :), потому что через пару часов вы уже не сможете гарантировать, что вышеуказанная ссылка будет ссылаться на тот сайт, на котором вы произвели поиск информации.

Я столкнулся с подобной проблемой при работе над одним проектом по сбору картинок с галерей изображений. Редиректеры вытворяли что-то нереальное: одна и таже ссылка на редиректере вела на разные сайты. И даже не надо было ждать пару часов - это происходило ежесекундно!

И это бы ничего, ведь даже file() умеет следовать редиректам. Но ведь заказчик потребовал в базе в отдельное поле писать конечный URL…

Решить данную проблему можно достаточно просто и вариантов ее решение есть несколько.

Самым разумным и быстрым из них будет использование функции curl_getinfo. И вообще я бы советовал всем программистам, которые используют библиотеку curl присмотреться к этой функции, ведь бывают ситуации, когда она может быть очень полезной.

Вот типичный пример использования данной функции в одном из моих классов:

function GetLastURL() {
	return curl_getinfo($this->curl_handler, CURLINFO_EFFECTIVE_URL);
}

В результате использования данной незатейливой функции вы получите последний урл, куда был перенаправлен ваш скрипт.

А что же делать, если вы не пользуетесь функционалом curl’a, а используете, например, file_get_contents()? Тогда прийдется немного поиграться в функцией stream_get_meta_data(). Вот типичное решение, которое я использовал в одном из своих проектов, где по ТЗ нельзя было использовать CURL:

$content = file_get_contents($url);
$fp = fopen($url,'r');
$meta_data = stream_get_meta_data($fp);
foreach($meta_data['wrapper_data'] as $response) {
	if (substr(strtolower($response), 0, 10) == 'location: ') {
		$last_url = substr($response, 10);
	}
}
fclose($fp);

Из данного кода я убрал все проверки и прочие функции, которые не имеют непосредственного отношения к обсуждаемой теме.

Как мы видим второе решение намного более громоздкое по количеству написанного кода, да и ограничений у него побольше будет. Но тем не менее каждый из вышеуказанных вариантов имеет право на жизнь.

Пользуйтесь ими на здоровье в своих парсерах, грабберах и анализаторах и не никогда не теряйтесь! ;)

Новая услуга. Сбор изображений, аудио- и видеофайлов.

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

Спецификой работы по сбору подобного контента является то, что БД имеет второстепенное значение, в то время как выдвигаются дополнительные требования к дисковой системе и скорости интернет-канала. Все эти особенности учтены в разработанной мной системе GrabVIA, предназначенной ИСКЛЮЧИТЕЛЬНО для сбора вышеуказанного контента.

ТТХ услуги:

  • 2 сервера (для очень крупных заказов возможно выделение до 4 серверов);
  • неограниченное время сбора информации (24/7/365);
  • сбор информации блоками до 100 Гб (100 Гб собрали -> отгрузили заказчику -> приступили к сбору очередных 100 Гб).

В качестве входных параметров для начала предоставления данной услуги могут выступать:

  • текстовый файл с адресами файлов для скачивания;
  • парсинг страниц для нахождения контента для скачивания.

Также возможна автоматизация работы:

  • вы даете мне параметры для доступа к FTP и весь собранный контент, с указанной вами регулярностью, синхронизируется с вашим сервером (также возможно настроить синхронизацию БД, если попутно со сбором контента будет собираться какая-то информация или параметры).

Обнаружение и удаление дублирующихся записей

В данной заметке я хочу затронуть вопрос нахождения и удаления дублирующихся записей в MySQL.

И это касается не только вопроса сбора информации. Предлагаемые методы будут очень полезны начинающих программистам, которые ищут оптимальное решение в данном вопросе.

Для того, чтобы было проще объяснять код я создам тестовую таблицу work с ней буду производить свои эксперименты.

Таблица work имеет следующую структуру:

CREATE TABLE work( 
 id mediumint(8) UNSIGNED NOT NULL AUTO_INCREMENT, 
 region varchar(255) NOT NULL, 
 job_mask varchar(255) NOT NULL, 
 job_group varchar(255) NOT NULL, 
 job_info text, 
 PRIMARY KEY(id));

Вдаваться в тонкости создания таблиц мы не будем, так как это не является целью данной статьи.

Предположим, что таблица work уже заполнена данными.

Теперь вам надо оставить в таблице только уникальные данные в поле job_info для каждого значения region, job_group, job_mask.

Первым делом вы должны определить есть ли у вас дублирующиеся данные и сколько их (ведь если их нет, то вам и делать ничего не надо). Для этого вам стоит выполнить следующий запрос:

SELECT COUNT(*) AS dub, region, job_group, job_mask, job_info 
FROM work GROUP BY region, job_group, job_mask, job_info HAVING dub>1;

В результате выполнения запроса вы увидите таблицу с уже знакомыми вам полями и с дополнительным полем dub, которое будет содержать количество дублирующихся записей.

Далее удаление можно организовать следующими путями:

  1. Циклический обход-1.
    Данный метод я называю “циклический обход-1″, потому что одну запись мы всегда оставляем не тронутой.

    Реализуется данные метод следующим способом:

     
     // соединение с БД  
     
     $query='SELECT COUNT(*) AS dub, region, job_group, job_mask, job_info 
     FROM work GROUP BY region, job_group, job_mask, job_info HAVING dub>1'; 
     $res=$db->query($query); 
     if(!$res) { 
    	echo $db->error(); 
     } else { 
    	while($row=$db->fetchAssoc($res)) { 
    	        $region=$row['region']; 
      	        $job_mask=$row['job_mask']; 
      	        $job_group=$row['job_group']; 
    	        $job_info=$row['job_info']; 
     
    	        $query="SELECT id FROM work 
    WHERE region='$region' AND job_mask='$job_mask' 
    AND job_group='$job_group' AND job_info='$job_info'"; 
     
    		$result=$db->query($query); 
    		if(!$result) { 
    			echo $db->error(); 
    		} else { 
    			$i=1; 
    			while ($subrow=$db->fetchArray($result)) { 
    				if ($i==1) { 
    				    $i++; 
    				    continue; 
    				} 
     
    				$query="DELETE FROM work 
                                                 WHERE id='{$subrow[0]}'"; 
    				$del_res=$db->query($query); 
    				if(!$del_res) { 
    					echo $db->error(); 
    				} else { 
    					$deleted++; 
    				} 
    				$i++; 
    			} 
    		} 
    	} 
    }

    Методы объекта $db, которые используются по ходу выполнения скрипта, являются методами моего стандартного абстрактного класса для доступа к БД. Имена методов очень тесно переплетаются с именами стандартных функций для работы с MySQL, поэтому я не вижу смысла останавливаться на этом более детально.

    В своих скриптах вы можете использовать привычные вам инструменты.

    Главным преимуществом данного решения есть простота. Однако есть и очень большой недостаток - низкая скорость выполнения. На больших таблицах с большим количеством записей данный скрипт будет выполняться очень долго. Так что не забудьте поставить лимит времени и лимит памяти побольше.

  2. Использование запроса SELECT.
    Второй вариант решения поставленной проблемы также достаточно распространен и предполагает некоторые знания в области SQL.Далее я приведу несколько запросов направленных на удаление дублирующихся записей и дам детальное их описание.

    Выборка уникальных данных в отдельную таблицу. Реализуется данный метод следующим образом:

     CREATE TABLE original 
     SELECT DISTINCT * FROM work ORDER BY id;

    Данный запрос создаст таблицу original с оригинальными значениями, выбранными из таблицы work.

    Также можно использовать следующую модификацию вышеуказанного выражения:

     CREATE TABLE original 
     SELECT * FROM work GROUP BY region, job_group, job_mask, job_info 
     ORDER BY id;

Дополнительная информация:

P.S. Думаю, что для начала этого инструментария будет достаточно. Но я не оставляю данную тему и надеюсь в ближайшем будущем предоставить дополнительную информацию по обработке дублирующихся записей в MySQL.

Яндекс ТИЦ

Иногда нам (веб-разработчикам и веб-оптимизаторам) нужно получить скриптом (потому что сервисов, которые это делаю за нас просто немеряно) такой параметр страницы как ТИЦ.

Введен этот чудный параметр был компанией Яндекс, поэтому все дружно его и называют Яндекс ТИЦ.

Для его нахождения я использую следующий простой скрипт:

function GetTCY($url) {		
   $url="http://bar-navig.yandex.ru/u?ver=2&show=32&url=".$url;
   $Result=file_get_contents($url);		
   return $Result ? (int) substr(strstr($Result, 'value="'), 7) : false;
}

Этой функции вы можете скармливать URL любого сайта, чей ТИЦ вы хотите узнать.

Данный метод основан на том же принципе, что и Яндекс.Бар.

Если вы уже очень любознательны, то в ответ на наш запрос Яндекс возвращает XML файл следующего вида:

<?xml version="1.0" encoding="windows-1251" ?>
<urlinfo>
 <url domain="www.net-safari.com"><![CDATA[/]]></url>
 <tcy rang="2" value="20"/>
 <topics>
 </topics>
 <textinfo>
 </textinfo>
</urlinfo>

Также вы можете скачать данный код в готовом виде отсюда.

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

Дополнительные сведения:

Парсинг выдачи Google. Практика.

Очень часто перед веб-мастерам встает задача парсинга выдачи поисковиков.

Это может быть нужно для того, чтобы:

  • автоматизировать процесс обработки какой-то статистической информации;
  • посчитать количество ссылающихся на ваш сайт ресурсов, определить их качество и прочие параметры;
  • найти урлы сайтов, которые натырили у вас контент (хотя тут лучше использовать что-то наподобии CopyScape, однако я заметил, что он иногда выдает бред);
  • определить частоту запросов.

И это лишь малый диапазон задач, в которых вам может понадобиться парсинг выдачи поисковиков. А точнее это то, что мне пришло в голову :)

А ведь еще у нас есть такие ребята как серые и черные оптимизаторы, которые при помощи поисковика ищут спамилки. Но это вообще отдельный разговор, который ничего общего с нашими благими целями не имеет :)

В данной статье я хотел бы рассказать как проще всего из выдачи Google доставать такие параметры как:

  • время генерации ответа (может понадобиться для статистики и расчета общего времени парсинга);
  • количество страниц, соответствующих вашему запросу (частота);
  • ну и собственно ссылки на конечные ресурсы.

Реализовывать механизм получения выдачи вы можете на чем угодно: curl, сокеты, fopen. Особой разницы для нас между ними нет. Предположим, что получать содержимое выдачи вы уже умеете (а если не умеете, то читать дальше вам просто незачем).

И вот тут начинаются вопросы.

1. Как достать из выдачи время генерации и количество страниц на данный запрос?

Я делаю это таким кодом (выдрано из тучи моих классов и гигантского ядра парсера, так что если найдете какую-то ошибку - напишите в комментах):

$blue_line_pcre = "(Результаты|Результати|Results)(.*)";
$parse_time_pcre= '\<b\>([\d\,\.]+)\<\/b\>\s[^\d]+$';
$parse_num_pcre	= '\<b\>([\d\,]+)\<\/b\>[^\d]+';
 
if(preg_match("/$blue_line_pcre/Ui",$Result,$matches)) {
    unset($matches[0]);
    if(!empty($matches[1])) {
        $BlueLine=preg_replace("/\&nbsp\;/",'',$matches[2]);
    } else {
        $BlueLine='';
    }
}
 
$find=$parse_num_pcre.$parse_time_pcre;
if(preg_match("/$find/Ui",$BlueLine,$matches)) {
    unset($matches[0]);
    $query_results_num=$matches[1];
    $query_time=$matches[2];
}

То есть, предполагается, что в переменной $Result хранится html код выдачи (и очено важно к этому коду не забыть применить

$Result=preg_replace("/[\n\r\t]/", '', $Result);

, чтобы потом не ломать голову почему половина регулярок не работают).
После выполнения моего скрипта в переменной $BlueLine будет html код синей полоски Гугла, в которой будет написано, что-то на подобии “Результаты 1 - 10 из примерно 87 300 000 для вакансии. (0,04 секунд)”.

В переменной query_results_num будет количество сайтов, подходящих под ваш запрос, а в переменной query_time - время генерации выдачи.

Из кода выше вы могли заметить, что регулярное выражение для нахождения кода “синей линии” имеет следующий непонятный вид:

(Результаты|Результати|Results)(.*)

Это сделано исключительно для того, чтобы с равной точностью парсить выдачу как google.com, так и google.ru и google.com.ua.

Заметка: результаты выдачи по одному и тому же запросу на разных локализованных версиях Google отличаются! Хоть иногда эту разницу тяжело обнаружить, но все же она есть. Можете на досуге поиграться с каким-то ВЧ запросом. Расскажете о результатах своих наблюдений.

Также некоторые вопросы могут вызывать регулярные выражения для нахождения времени генерации и количества страниц:

$parse_time_pcre = '\<b\>([\d\,\.]+)\<\/b\>\s[^\d]+$';
$parse_num_pcre	= '\<b\>([\d\,]+)\<\/b\>[^\d]+';

Для чего я использую вот такие конструкции [\d\,\.]? Исключительно для совместимости. Делаю я это потому, что разделителем целой и дробной части в нашей стране является “.”, а в США - “,”. В регулярном выражении для количества страниц вы также можете видеть символ “\,” - это используется для того, чтобы правильно находить количество страниц, так как американцы разделяют тысячи этим знаком.

2. Как достать ссылки из выдачи?

Эта даже проще чем выдирать параметры. Я делаю это при помощи следующего кода:

$find = $parse_links_pcre = "<a\shref=\"([^\"]+)\".*sclass=l";
if(preg_match_all("/$find/Ui",$Result,$matches)) {
    unset($matches[0]);
    $links=$matches[1];
}

В массиве $links будут все ссылки, найденный на текущей странице. Недостатки у такой унификации тоже есть: в результирующем массиве вы получите все ссылки “Translate” и этот метод не очень-то и быстро работает из-за использования в регулярном выражении “.*”. Но это место всегда можно подрихтовать под ваши конкретные требования. В оригинальном виде я тоже не особо часто использую данное регулярное выражение. Но есть также и плюсы у этого кода: он не загребает ссылки с оплаченных объявлений, а они, как вы знаете, вылазят на каждой странице поиска и могут очень сильно нарушить ваши статистические исследования или сбор ссылок.

Спасибо, что дочитали до конца. Рад буду выслушать ваши предложения и дополнения.

Определение Google PR

Очень часто на разнообразных форумах я натыкаюсь на вопросы как на php реализовать скрипт подсчета Google PR (PageRank) сайта.

Работоспособный вариант вы можете скачать отсюда.

Этот код является наиболее простым и быстрым. Его недостатком является то, что он не разрешает определять PR на разных датацентрах компании Google, что может быть полезно в ряде случаев.

Данный метод основан на парсинге выдачи поисковика Google.

UPD. Заплатка для владельцев 64 разрядных машин.

Дополнительные ссылки:

Работа с cookies

Очень часто новички в области грабинга/парсинга сайтов задают один и тот же вопрос:
“Как максимально просто отлавливать и устанавливать cookie?”.

Используя curl работа с кукисами стает вообще максимально простой. Вам стоит всего лишь использовать следующий код:

if(is_writable($CookieFile)) {
curl_setopt($curl_handler, CURLOPT_COOKIEFILE, $CookieFile);
curl_setopt($curl_handler, CURLOPT_COOKIEJAR, $CookieFile);
} else {
die('Файл не является записываемым: '.$CookieFile);
}

Для вас может быть новой такая опция как CURLOPT_COOKIEJAR и это неудивительно. Например, в моей версии официальной документации к PHP она не описана.

Что же делает данный кусок кода?

Поэтапно:

  • Проверяет можно ли писать в указанный в переменной $CookieFile файл
  • Если нельзя - выводит ошибку
  • Если можно - устанавливает кукисы из файла $CookieFile и устанавливает запись кукисов в данный файл

Грубо говоря, при помощи данного кода вы сохраняете все кукисы в файл и потом их используете.

Это может быть полезно когда:

  • на сайте используется сессионная модель взаимодействия с посетителями
  • когда аутентификация основана на информации из кукисов
  • когда в кукисы записываются какие-либо переменные, необходимые для дальнейшей работы на сайте

Дополнительные ссылки: