Методы входа на сайты программно - PullRequest
4 голосов
/ 20 августа 2009

Я пытаюсь автоматизировать вход в Photobucket для использования API для проекта, который требует автоматической загрузки фотографий с использованием сохраненных учетных данных.

API генерирует URL-адрес для входа в систему, и с помощью Firebug я могу видеть, какие запросы и ответы отправляются / принимаются.

У меня вопрос, как я могу использовать HttpWebRequest и HttpWebResponse, чтобы имитировать то, что происходит в браузере в C #?

Можно ли использовать компонент веб-браузера в приложении C #, заполнить поля имени пользователя и пароля и отправить логин?

Ответы [ 4 ]

4 голосов
/ 20 августа 2009

Вот как я это решил:

public partial class Form1 : Form {
        private string LoginUrl = "/apilogin/login";
        private string authorizeUrl = "/apilogin/authorize";
        private string doneUrl = "/apilogin/done";

        public Form1() {
            InitializeComponent();
            this.Load += new EventHandler(Form1_Load);
        }

        void Form1_Load(object sender, EventArgs e) {
            PhotobucketNet.Photobucket pb = new Photobucket("pubkey","privatekey");
            string url = pb.GenerateUserLoginUrl();
            webBrowser1.Url = new Uri(url);
            webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
        }

        void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
            if (e.Url.AbsolutePath.StartsWith(LoginUrl))
            {
                webBrowser1.Document.GetElementById("usernameemail").SetAttribute("Value","some username");
                webBrowser1.Document.GetElementById("password").SetAttribute("Value","some password");
                webBrowser1.Document.GetElementById("login").InvokeMember("click");
            }

            if (e.Url.AbsolutePath.StartsWith(authorizeUrl))
            {
                webBrowser1.Document.GetElementById("allow").InvokeMember("click");
            }

            if (e.Url.AbsolutePath.StartsWith(doneUrl))
            {
                string token = webBrowser1.Document.GetElementById("oauth_token").GetAttribute("value");
            }
        }
    }

захват токена в последнем блоке if - это то, что необходимо для продолжения использования API. Этот метод прекрасно работает для меня, так как, конечно, код, который нуждается в этом, будет работать на Windows, поэтому у меня нет проблем с порождением процесса загрузки этого отдельного приложения для извлечения токена.

4 голосов
/ 20 августа 2009

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

Я быстро обнаружил, что выполнение HttpWebRequest / HttpWebResponse с нуля действительно было более низким уровнем, чем я хотел иметь дело. Мои инструменты полностью основаны на HtmlAgilityPack Саймона Мурье. Это отличный набор инструментов. Это делает большую работу для вас тяжелой и делает разбор извлеченного HTML действительно простым. Если вы можете качать запросы XPath, HtmlAgilityPack - это то место, с которого вы хотите начать. Он также отлично справляется с HTML с плохой формой!

Вам все еще нужен хороший инструмент для отладки. Помимо того, что вы имеете в своем отладчике, возможность проверять http / https-трафик, когда он идет по проводам, бесценна. Поскольку ваш код будет выполнять эти запросы, а не ваш браузер, FireBug не сильно поможет в отладке вашего кода. Существуют всевозможные инструменты анализатора пакетов, но для отладки HTTP / HTTPS я не думаю, что вы можете победить простоту использования и мощь Fiddler 2 . В новейшей версии даже есть плагин для Firefox, позволяющий быстро перенаправлять запросы через фиддлеры и обратно. Поскольку он также может действовать как цельный HTTPS-прокси, вы также можете проверять свой HTTPS-трафик.

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

Обновление: Добавлен пример кода ниже. Это взято из не слишком большого класса "Session", который входит на веб-сайт и хранит связанные с вами файлы cookie. Я выбрал это, потому что он делает больше, чем просто код «пожалуйста, извлеките эту веб-страницу для меня», плюс в нем есть строка-две XPath-запроса к конечной странице назначения.

public bool Connect() {
   if (string.IsNullOrEmpty(_Username)) { base.ThrowHelper(new SessionException("Username not specified.")); } 
   if (string.IsNullOrEmpty(_Password)) { base.ThrowHelper(new SessionException("Password not specified.")); }

   _Cookies = new CookieContainer();
   HtmlWeb webFetcher = new HtmlWeb();
   webFetcher.UsingCache = false;
   webFetcher.UseCookies = true;

   HtmlWeb.PreRequestHandler justSetCookies = delegate(HttpWebRequest webRequest) {
      SetRequestHeaders(webRequest, false);
      return true;
   };
   HtmlWeb.PreRequestHandler postLoginInformation = delegate(HttpWebRequest webRequest) {
      SetRequestHeaders(webRequest, false);

      // before we let webGrabber get the response from the server, we must POST the login form's data
      // This posted form data is *VERY* specific to the web site in question, and it must be exactly right,
      // and exactly what the remote server is expecting, otherwise it will not work!
      //
      // You need to use an HTTP proxy/debugger such as Fiddler in order to adequately inspect the 
      // posted form data. 
      ASCIIEncoding encoding = new ASCIIEncoding();
      string postDataString = string.Format("edit%5Bname%5D={0}&edit%5Bpass%5D={1}&edit%5Bform_id%5D=user_login&op=Log+in", _Username, _Password);
      byte[] postData = encoding.GetBytes(postDataString);
      webRequest.ContentType = "application/x-www-form-urlencoded";
      webRequest.ContentLength = postData.Length;
      webRequest.Referer = Util.MakeUrlCore("/user"); // builds a proper-for-this-website referer string

      using (Stream postStream = webRequest.GetRequestStream()) {
         postStream.Write(postData, 0, postData.Length);
         postStream.Close();
      }

      return true;
   };

   string loginUrl = Util.GetUrlCore(ProjectUrl.Login); 
   bool atEndOfRedirects = false;
   string method = "POST";
   webFetcher.PreRequest = postLoginInformation;

   // this is trimmed...this was trimmed in order to handle one of those 'interesting' 
   // login processes...
   webFetcher.PostResponse = delegate(HttpWebRequest webRequest, HttpWebResponse response) {
      if (response.StatusCode == HttpStatusCode.Found) {
         // the login process is forwarding us on...update the URL to move to...
         loginUrl = response.Headers["Location"] as String;
         method = "GET";
         webFetcher.PreRequest = justSetCookies; // we only need to post cookies now, not all the login info
      } else {
         atEndOfRedirects = true;
      }

      foreach (Cookie cookie in response.Cookies) {
         // *snip*
      }
   };

   // Real work starts here:
   HtmlDocument retrievedDocument = null;
   while (!atEndOfRedirects) {
      retrievedDocument = webFetcher.Load(loginUrl, method);
   }


   // ok, we're fully logged in.  Check the returned HTML to see if we're sitting at an error page, or
   // if we're successfully logged in.
   if (retrievedDocument != null) {
      HtmlNode errorNode = retrievedDocument.DocumentNode.SelectSingleNode("//div[contains(@class, 'error')]");
      if (errorNode != null) { return false; }
   }

   return true; 
}


public void SetRequestHeaders(HttpWebRequest webRequest) { SetRequestHeaders(webRequest, true); }
public void SetRequestHeaders(HttpWebRequest webRequest, bool allowAutoRedirect) {
   try {
      webRequest.AllowAutoRedirect = allowAutoRedirect;
      webRequest.CookieContainer = _Cookies;

      // the rest of this stuff is just to try and make our request *look* like FireFox. 
      webRequest.UserAgent = @"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3";
      webRequest.Accept = @"text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
      webRequest.KeepAlive = true;
      webRequest.Headers.Add(@"Accept-Language: en-us,en;q=0.5");
      //webRequest.Headers.Add(@"Accept-Encoding: gzip,deflate");
   }
   catch (Exception ex) { base.ThrowHelper(ex); }
}
2 голосов
/ 20 августа 2009

Для входа на веб-сайты можно использовать встроенный WebbrowserControl. Но, как вы видите в примере, вам придется определить имя элемента управления раньше.

private void webBrowserLogin_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {

        if (webBrowserLogin.Url.ToString() == WebSiteUrl)
        {
            foreach (HtmlElement elem in webBrowserLogin.Document.All)
            {

                if (elem.Name == "user_name")              // name of the username input
                {
                    elem.InnerText = UserName;               
                }

                if (elem.Name == "password")               // name of the password input
                {
                    elem.InnerText = Password;                
                } 

            }

            foreach (HtmlElement elem in webBrowserLogin.Document.All)
            {

                if (elem.GetAttribute("value") == "Login")
                {
                    elem.InvokeMember("Click");
                }
            }
        }
    }
0 голосов
/ 17 июля 2012

Ознакомьтесь с классом Rohit BrowserSession, который он описал здесь часть 2 здесь ). Основан на HtmlAgilityPack, но выполняет скучную работу по заполнению данных POST из FORM.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...