Как программно авторизоваться в vkontakte (vk.com) на C#

от автора

image

Как-то давно видел на Хабре статью, как авторизоваться в yandex. Вдохновившись ею, решил написать аналогичную для «Вконтакте».
Сразу скажу, что в целом подход похожий, но есть и значительные отличия.

В основе всего используются HttpWebRequest и HttpWebResponse.

Шаг 1

Для сокращения кода подключаем:

using System.Collections; using System.Net; using System.IO; using System.Text.RegularExpressions; using System.Drawing; 

Создаем класс, назовем его Net, в нем метод GetHtml:

class Net     {                  string remixsid;  //Id сессии         public string lastCookies; //Куки          public string GetHtml(string url, string postData) //Возвращает содержимое поданной страницы         {             string HTML = "";              Regex rex1 = new Regex("remixsid=(.*?);", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);             if (url == "0") return "0"; //Проверка на ошибку             HttpWebRequest myHttpWebRequest =(HttpWebRequest)HttpWebRequest.Create(url);             //myHttpWebRequest.Proxy = new WebProxy("127.0.0.1", 8888); //В перспективе можно использовать прокси             if (!String.IsNullOrEmpty(postData)) myHttpWebRequest.Method = "POST";             myHttpWebRequest.Referer = "https://vk.com";             myHttpWebRequest.UserAgent = "Mozila/14.0 (compatible; MSIE 6.0;Windows NT 5.1; SV1; MyIE2;";             myHttpWebRequest.Accept = "image/gif, image/x-xbitmap, image/jpeg,image/pjpeg, application/x-shockwave-flash,application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword";             myHttpWebRequest.Headers.Add("Accept-Language", "ru");             myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";             myHttpWebRequest.KeepAlive = false;              // передаем Сookie, полученные в предыдущем запросе             if (!String.IsNullOrEmpty(this.remixsid))             {                 lastCookies = "remixchk=5;remixsid=" + this.remixsid;             }             if (!String.IsNullOrEmpty(lastCookies))             {                 myHttpWebRequest.Headers.Add(System.Net.HttpRequestHeader.Cookie, lastCookies);             }             // ставим False, чтобы при получении кода 302, не делать              // автоматического перенаправления             myHttpWebRequest.AllowAutoRedirect = false;                       // передаем параметры             string sQueryString = postData;             byte[] ByteArr = System.Text.Encoding.GetEncoding(1251).GetBytes(sQueryString); //Вконтакте использует кирилическую кодировку             try             {                 if (!String.IsNullOrEmpty(postData))                 {                     myHttpWebRequest.ContentLength = ByteArr.Length;                     myHttpWebRequest.GetRequestStream().Write(ByteArr, 0, ByteArr.Length);                 };                  // делаем запрос                 HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();                 StreamReader myStreamReader;                                  //Сохраняем Cookie                  lastCookies = String.IsNullOrEmpty(myHttpWebResponse.Headers["Set-Cookie"]) ? "" : myHttpWebResponse.Headers["Set-Cookie"];                 Match matc1 = rex1.Match(lastCookies);                                  //Если есть имя сессии, то подменяем Cookie                  if (matc1.Groups.Count == 2) { this.remixsid = matc1.Groups[1].ToString(); lastCookies = "remixchk=5;remixsid=" + this.remixsid; }                 if (myHttpWebResponse.Headers["Content-Type"].IndexOf("windows-1251") > 0)                 {                     myStreamReader = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.GetEncoding("windows-1251"));                 }                 else                 {                     myStreamReader = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.UTF8);                 }                 HTML = myStreamReader.ReadToEnd();                 if (HTML == "") //Проверяем на редирект                 {                     HTML = this.GetHtml(myHttpWebResponse.Headers["Location"].ToString(), "");                  }             }             catch (Exception err)             {                 //Ошибка в чтении страницы                 return "0";             }             return HTML;         }     } 

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

Шаг 2

Чтобы воспользоваться данным методом и авторизоваться, нужно создать объект http и отправить запрос на авторизацию.

Net http = new Net(); //Создаем объект string post = "email=" + this.login + "&pass=" + this.password + "&q=1&act=login&q=1&al_frame=1&expire=&captcha_sid=&captcha_key=&from_host=vk.com&from_protocol=http&ip_h=4e78766a2890ac1115&quick_expire=1";              string html = http.GetHtml("https://vk.com/", ""); html = http.GetHtml("https://login.vk.com/?act=login", post); 

Первый запрос просто получает главную страницу, второй отправляет данные с логином и паролем.

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

<script type="text/javascript"> var _ua = navigator.userAgent; var locDomain = 'vk.com'.match(/[a-zA-Z]+\.[a-zA-Z]+\.?$/)[0]; if (/opera/i.test(_ua) || !/msie 6/i.test(_ua) || document.domain != locDomain) {   document.domain = locDomain; } parent.__qlClear(); parent.onLoginDone('/id62983254'); </script> 

Шаг 3

Вытягиваем из этого кода Id:

Regex rex4 = new Regex("parent\\.onLoginDone\\(\\'(.*?)\\'\\)", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); matc4 = rex4.Match(html); this.userid = matc4.Groups[1].ToString().Replace("/id", ""); 

Шаг 4

Прогружаем страничку пользователя и проверяем успешность регистрации:

html = http.GetHtml("https://vk.com/id" + this.userid, ""); int status = Testlogin(html); 

status==1 — авторизация прошла успешно
status==2 — аккаунт заблокирован
status==3 — ошибка логина или пароля
status==4 — требуется переавторизация

Код метода проверки на авторизацию и на капчу:

        private int Testlogin(string html)         {             if (html.IndexOf("login?act=blocked") > 0) { this.status = 2; return 2; }             if (html.IndexOf("onLoginFailed") > 0) { this.status = 3; return 3; }             Regex rex1 = new Regex("href=\"\\/edit\"", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);             Match matc1 = rex1.Match(html);             if (matc1.Groups[0].Length == 0) { this.status = 4; return 4; }             this.status = 1;             return 1;         }         private string TestCaptch(string html)         {              Regex rex1 = new Regex("captcha_sid\\\":\\\"(\\d*)\\\"", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);             Match matc1 = rex1.Match(html);             if (matc1.Groups[1].Length == 0) return "0";             return matc1.Groups[1].ToString();         } 

Метод TestCaptch возвращает Id капчи, которую можно использовать в дальнейшем разгадывании.

Шаг 5

Разгадываем капчу.

В класс Net добавляем еще один метод:

 public Image GetImg(string url, Image image) //Возвращает изображение         {             string postData = "";             string HTML = "";             HttpWebRequest myHttpWebRequest =               (HttpWebRequest)HttpWebRequest.Create(                 url);             //myHttpWebRequest.Proxy = new WebProxy("127.0.0.1", 8888);             if (!String.IsNullOrEmpty(postData)) myHttpWebRequest.Method = "POST";             myHttpWebRequest.Referer = "http://vk.com";             myHttpWebRequest.UserAgent = "Mozila/4.0 (compatible; MSIE 6.0;Windows NT 5.1; SV1; MyIE2;";             myHttpWebRequest.Accept = "image/gif, image/x-xbitmap, image/jpeg,image/pjpeg, application/x-shockwave-flash,application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword";             myHttpWebRequest.Headers.Add("Accept-Language", "ru");             myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";             myHttpWebRequest.KeepAlive = false;              // передаем Cookie, полученные в предыдущем запросе                     if (!String.IsNullOrEmpty(this.remixsid))             {                 lastCookies = "remixchk=5;remixsid=" + this.remixsid;             }             if (!String.IsNullOrEmpty(lastCookies))             {                 myHttpWebRequest.Headers.Add(HttpRequestHeader.Cookie, lastCookies);             }             // ставим False, чтобы при получении кода 302, не делать              // автоматического перенаправления             myHttpWebRequest.AllowAutoRedirect = true;                       // передаем параметры             string sQueryString = postData;             byte[] ByteArr = System.Text.Encoding.GetEncoding(1251).GetBytes(sQueryString);              if (!String.IsNullOrEmpty(postData))             {                 myHttpWebRequest.ContentLength = ByteArr.Length;                 myHttpWebRequest.GetRequestStream().Write(ByteArr, 0, ByteArr.Length);             };              // делаем запрос              try             {                 HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();                 StreamReader myStreamReader;                                  if (myHttpWebResponse.Headers["Content-Type"].IndexOf("windows-1251") > 0)                 {                     myStreamReader = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.GetEncoding("windows-1251"));                 }                 else                 {                     myStreamReader = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.UTF8);                 }                 image = Image.FromStream(myHttpWebResponse.GetResponseStream());                 HTML = myStreamReader.ReadToEnd();                 myHttpWebResponse.Close();                 if (HTML == "") HTML = myHttpWebResponse.Headers["Location"].ToString();             }             catch (Exception err)             {                 //Ошибка в чтении страницы                 return image;             }             return image;         } 

В принципе метод похож на GetHtml, за исключением того, что он возвращает объект Image с запрашиваемой капчей.

Выводим картинку на нашу форму:

        public void Addcap(string sid)         {             this.img = "http://vk.com/captcha.php?sid=" + sid;                        Image image = net.GetImg(img, pictureBox1.Image);             pictureBox1.Image = image; //Заранее созданный объект PictureBox, в котором будет видна капча          } 

Если при отправке какого-нибудь запроса, наша проверка на капчу дала положительный результат, то нам нужно разгадать капчу и отправить запрос заново, но добавить к нему два параметра &captcha_key= и &captcha_sid=

К слову, разгадать капчу можно как в ручную, так и прикрутить код с Antigate.

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

post = "act=post&hash=" + hash + "&message=" + textu + "&al=1&captcha_key=" + captcha_key + "&captcha_sid=" + captcha_sid + "&facebook_export=&fixed=&friends_only=&from=¬e_title=&official=&signed=&status_export=&attach1_type=" + attachtype + "&attach1=" + attach + "&to_id=" + this.userid + "&type=all&url=" + System.Web.HttpUtility.UrlEncode(sendurl) + "&title=" + title + "&photo_url=" + System.Web.HttpUtility.UrlEncode(img); 

captcha_key — разгаданная капча
captcha_sid — id капчи
sendurl — ссылка в теле сообщения
img — адрес картинки для ссылки
textu — сам текст сообщения

Есть еще параметр Hash, но его мне сгенерить не удалось, потому я достаю его регуляркой, предварительно зайдя на страницу группы.

Regex rex2 = new Regex("\"post_hash\":\"(.*?)\"", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); Match matc2 = rex2.Match(html); string hash = matc2.Groups[1].ToString(); 

P.S.
Возможно, кто-то скажет, что логичней было бы использовать API контакта, но, как показала практика, если использовать сразу несколько авторизаций с помощью API c одного IP, то через какое-то время данные аккаунты банятся. А в этом случае мы можем полностью сэмулировать действия реального пользователя через обычный браузер.

Но даже при таком подходе я бы советовал часть запросов, таких как получение информации о пользователях по их Id, выполнять через API, тем более данная операция не требует авторизации.

Если кому-то пригодится, могу в личку скинуть готовый класс под работу с Antigate. Он большой, и к теме напрямую не относится, потому тут выкладывать не стал.

ссылка на оригинал статьи http://habrahabr.ru/post/170467/


Комментарии

2 комментария на ««Как программно авторизоваться в vkontakte (vk.com) на C#»»

  1. Аватар пользователя Александр
    Александр

    Отличная статья, но не могли бы подсказать как реализовать отправку сообщения на чужую стену, понимаю что уже прошло немало времени и все же. Что Вы использовали для анализа трафика (как создали данные запросы )?

    Заранее спасибо

  2. Аватар пользователя Александр
    Александр

    Все сам разобрался, ещё раз спасибо

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *