developing.name Решение проблем разработки сайтов. uncat php javascript jquery ajax html other C#

Кукисы в C#

03.03.2009, 20:07
Рубрика: C#

Cookies в CookieContainer отличная штука, только вот проблемная. Статья для столкнувшихся с этим и для тех, кто не хочет повторять чужие ошибки. ;)

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

Все хорошо, но суть проблемы подкралась незаметно, и очень долго пришлось выискивать ошибку, учитывая, что по прошлым разработкам такого замечено не было. Как понимаете проблема в обработке кукисов. Не верилось в это до последнего момента, пока до конца не сравнились данные в контейнерах и в хидерах (Set-Cookies, Cookies).

Оказалось, что контейнер (CookieContainer) захватывает не все данные (переменные со значениями). Потому и авторизация бота не проходила.

Объем кукисов приличный, посему не было никакого желания выяснять конкретную причину (время -деньги). Куда проще было написать свой парсер кукисов. То есть, брать контейнер не из response, а просто парсить Set-Cookies из headers и добавлять в контейнер.

public static MatchCollection GetCookies(string cookiesstr)
{
    string HeadersCookie = Regex.Replace(cookiesstr, @"\s?expires=(.*?)GMT;?", "",
        RegexOptions.Compiled | RegexOptions.IgnoreCase);

    Regex rx = new Regex(@"(?<name>[^=]+)=(?<value>[^;]*);([^,]*domain=(?<domain>[^;]+);)?[^,]*,?",
        RegexOptions.Compiled | RegexOptions.IgnoreCase);

    MatchCollection matches = rx.Matches(HeadersCookie);

    return matches;
}

В Set-Cookies все параметры разделяются запятыми. Но запятая содержится в expires, а сроки хранения кукисов нам не важны, потому удаляем expires вообще.

Далее берем параметр, его значение и домен к которому действителен сей кукис. Пусть всегда будет в корне, другого нам не надо. И в matches получаем набор отпарсеных данных. Кстати, для проверки регулярных выражений понравился RegexTuner.NET (http://sharp-developer.net/), The Regex Coach тоже не плох, но первый специально для .NET, со своими фишками.

Остается добавить/обновить в контейнере.

foreach (Match match in matches)
{
    GroupCollection groups = match.Groups;

    string path = string.Empty;
    string domain = string.Empty;

    if (groups["domain"].Value == string.Empty)           // если домена нет, то берем тот который запрашиваем
        domain = wrequest.RequestUri.Host.ToString();
    else
        domain = groups["domain"].Value;

    cookies.Add(new Cookie(groups["name"].Value, groups["value"].Value, "/", domain));
}

И нет проблем. Заработало, кукисы не теряются, все записывается, все учитывается. Возможно (!? не будем утверждать), что сервис посылал специально некоторые кривые кукисы, дабы избежать нахождения ботов. Хотя как же понимает такие кукисы обычные браузеры?!

Бот побегал по сайту, и опять загнулся. Опять мучения, опять страдания. Но нам все нипочем. Есть еще одна фишка:

Имея кукис к домену “.domain.com”, он будет учитываться к “sub.domain.com”, но НЕ будет работать с “test.sub.domain.com”, “very.test.sub.domain.com” и т.д. Странно конечно, но пришлось добавить:

if (domain == ".domain.com")
{
    cookies.Add(new Cookie(groups["name"].Value, groups["value"].Value, "/", ".sub.domain.com"));
    cookies.Add(new Cookie(groups["name"].Value, groups["value"].Value, "/", ".test.sub.domain.com"));
}

Вот такой итог:

if (wresponse.Headers["Set-Cookie"] != null)
{
    MatchCollection matches = Parser.GetCookies(wresponse.Headers["Set-Cookie"].ToString());

    foreach (Match match in matches)
    {
        GroupCollection groups = match.Groups;

        string path = string.Empty;
        string domain = string.Empty;

        if (groups["domain"].Value == string.Empty)           // если домена нет, то берем тот который запрашиваем
            domain = wrequest.RequestUri.Host.ToString();
        else
            domain = groups["domain"].Value;

        if (domain == ".domain.com")
        {
            cookies.Add(new Cookie(groups["name"].Value, groups["value"].Value, "/", ".sub.domain.com"));
            cookies.Add(new Cookie(groups["name"].Value, groups["value"].Value, "/", ".test.sub.domain.com"));
        }

        cookies.Add(new Cookie(groups["name"].Value, groups["value"].Value, "/", domain));
    }
}

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

cookies = new CookieContainer(900, 100, 8192);

wrequest.CookieContainer = cookies;

ВНИМАНИЕ! К сожалению, функцию GetCookies приходилось править под другие проекты.

Ссылки по теме:

  • 03.03.2009 - Кукисы в C#
  •  

    Комментарии



    Жук (05.11.2009, 00:31:48) пишет:

    Спасибо! Ваша статья помогла понять принципы работы. Хотя сделал я по другому малость:

    private static CookieCollection GetCookies(CookieContainer container, Uri requestUri)
           {
               CookieCollection result = container.GetCookies(requestUri);

               if (container.Count > result.Count)
               {
                   try
                   {
                       CookieCollection addCookies = new CookieCollection();

                       Uri subUri = new Uri(requestUri.Scheme + ″://sub.″ + requestUri.Host);
                       CookieCollection subResult = container.GetCookies(subUri);
                       foreach (Cookie cookie in subResult)
                       {
                           addCookies.Add(new Cookie(cookie.Name, cookie.Value, ″/″, requestUri.Host));
                       }

                       if (addCookies.Count > 0)
                           container.Add(addCookies);
                   }
                   catch
                   {
                       //TODO To log
                   }
               }

               return result;
           }

     

    Имя*:
    e-mail*:
    URL:
    captcha*
    Текст*:
    • c# Cookie
    • кукисы
    • c# cookies
    • CookieContainer
    • cookies c#
    • Cookie C#
    • regex c#
    • C# работа с cookie
    • куки c#
    • C# куки
    • C# работа с cookies
    • работа с Cookies C#
    • c# Regex
    • работа с cookie c#
    • C# Match
    • C# change hosts
    • работа с cookie в c#
    • cookie set cookie c#
    • c# CookieContainer
    • как работать с кукисами
    • set
    • c# set
    • как написать парсер на c#
    • CookieContainer.SetCookies
    • c# replace
    • работа с cookies в C#
    • работа с куки c#
    • c# html parser
    • c# работа с html
    • парсер на C#
    • парсинг html C#
    • c# получить cookie
    • разобрать cookie
    • c#develop
    • что такое кукисы
    • GetCookies CookieContainer
    • set cookie c#
    • new Cookie c#
    • c# parse cookies
    • cookies авторизация c#
    • c# get cookies
    • написать бот c#
    • парсинг html на C#
    • Headers["Cookie"] c#
    • авторизация на сайт в C# и cookiecontainer
    • cookies и C#
    • GetCookies
    • MatchCollection c#
    • asp c# cookies
    • C# использование cookies

    jQuery plugins:
    • pro.scroll - графический скролл для div
    • pro.tips - всплывающие подсказки
    Powered by PRO