Все наверное уже знают как
получить содержимое URL с
помошью методов GET или POST
используя классы WebRequest/WebResponse.
Но бывают моменты, когда
этого мало, например, когда
для доступа к так желаемым
данным необходимо пройти
предварительно
аутентификацию.
Вот и я столкнулся с
подобной задачей. Нужно было
программно обработать
порядка тысячи страниц и
получить из них некоторые
значения. Но для получения
этих страниц необходимо было
пройти аутентификацию.
Поиски решения данной
задачи в инете ни к чему не
привели и пришлось в итоге
заняться этим вплотную
самому. Некоторое время было
потрачено на исследования и
тестирование и результат не
заставил себя долго ждать.
Итак для начала
теоретические выкладки.
Обычно аутентификация
пользователя на сайте
происходит следующим
образом:
- Пользователь вводит
свои данные и отсылает
форму на сервер.
- Сервер проверяет
есть ли пользователь с
такими данными в базе и
если все ок – возвращает
в ответе какие-то куки
(неважно какие, важно,
что по ним потом
проверяется,
аутентифицирован ли
пользователь).
- При запросе
защищенной страницы
сервер проверяет наличие
нужных кук и если все ок
– возвращает страницу.
Значит, судя по данному
алгоритму все, что нужно
добавить к стандартному
методу получения страниц по
URL – это добавить в него
поддержку кук. Ну раз надо –
значит надо :). Ниже
представлен код, решающий
данную задачу, с
комментариями.
Для начала необходимо
сходить на нужный сайт
ручками и посмотреть в коде
переменные, которые
посылаются формой при
логине. Допустим их 3 –
login, pwd и action (всегда
равный login). Теперь можно
начинать программировать.
Вначале все выглядит как
в известном примере
получения данных по POST
запросу – создается
экземпляр класса WebRequest
с нужным URL,
устанавливаются свойства
ContentType и Method,
создается строка парамеров,
которую необходимо будет
отправить на данный URL и
затем отсылается туда.
Возвращаемый сервером
результат принимается в
экземпляр класса
HttpWebResponse.
HttpWebResponse result = null;
HttpWebRequest req = (HttpWebRequest) HttpWebRequest.Create("(URL для авторизации)");
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
byte[] SomeBytes = null;
string FormParams = "Login=(имя пользователя)&Password=(пароль)&action=login";
SomeBytes = Encoding.UTF8.GetBytes(FormParams);
req.ContentLength = SomeBytes.Length;
Stream newStream = req.GetRequestStream();
newStream.Write(SomeBytes, 0, SomeBytes.Length);
newStream.Close();
result = (HttpWebResponse) req.GetResponse();
Теперь начинается самое
интересное. Возвращаемые
сервером куки передаются в
заголовке ответа сервера с
именем Set-Cookie. И теперь
их необходимо получить из
этого заголовка, создать на
их основе правильный
экземпляр класса
CookieContainer и
использовать его в
дальнейших запросах.
Конечно для начала
неплохо бы и проверить
удачно ли прошла
авторизация.
string[] cookieVal = null;
if(result.Headers["Set-Cookie"] != null)
cookieVal = result.Headers["Set-Cookie"].Split(new char[] {','});
Stream ReceiveStream = result.GetResponseStream();
Encoding encode = Encoding.GetEncoding("utf-8");
StreamReader sr = new StreamReader( ReceiveStream, encode );
string answer = sr.ReadToEnd();
sr.Close();
result.Close();
Будем считать что все в
порядке и пара имя/пароль
правильные (все таки в
данной статье обсуждается
несколько иная тема). Теперь
необходимо создать
правильный экземпляр
CookieContainer.
Возвращаемая в заголовке
«Set-Cookie» строка содержит
в себе список кук,
разделенных запятыми. Каждая
же кука имеет следующий
формат: «(имя
куки)=(значение
куки);path=(путь
куки);domain=(домен
куки);Expires=(дата, до
которой кука хранится)».
Основываясь на этих данных и
создается контейнер с
возвращенными куками.
CookieContainer cookie = new CookieContainer();
foreach(string cook in cookieVal)
{
string[] cookie1 = cook.Split(new char[] {';'});
if(cookie1.Length < 2)
continue;
cookie.Add(new Cookie(cookie1[0].Split(new char[] {'='})[0], cookie1[0].Split(new char[] {'='})[1],
cookie1[1].Split(new char[] {'='})[1], cookie1.Length > 2 ? cookie1[2].Split(new char[] {'='})[1] : ""));
}
Проверка на длину массива
куки вставлена из-за того,
что дата в возвращаемой в
Set-Cookie строке
представлена в полном
формате и имеет символ «,» в
своем составе.
Соответственно может
получиться так, что
некоторые элементы массива
cookieVal будут содержать
часть даты. А так как нас не
особо интересует Expires
дата – мы просто отбрасываем
данные строки.
Теперь есть все для
получения защищенных
страниц. Для присоединения
CookieContainer к экземпляру
класса WebRequest
используется свойство
CookieContainer данного
класса. Ну а дальше все, как
по теории:
HttpWebRequest req1 = (HttpWebRequest) HttpWebRequest.Create((URL защищенной страницы));
req1.UserAgent = "Mozilla/4.0+(compatible;+MSIE+5.01;+Windows+NT+5.0)";
//Вот оно - важное дополнение.
req1.CookieContainer = cookie;
req1.Method = "GET";
HttpWebResponse result1 = (HttpWebResponse) req1.GetResponse();
Stream ReceiveStream1 = result1.GetResponseStream();
StreamReader sr = new StreamReader( ReceiveStream1, encode );
string html = sr.ReadToEnd();
result1.Close();
Дело сделано :).
PS. Через 3 дня после
моих мучений с данным
алгоритмом администрация
сервера, с которого я брал
страницы, сделала их
общедоступными :). |