2010-08-12 12 views
12

Estoy tratando de usar la clase BrowserSession de Rohit Agarwal junto con HtmlAgilityPack para iniciar sesión y navegar posteriormente por Facebook.Uso de BrowserSession y HtmlAgilityPack para iniciar sesión en Facebook a través de .NET

Anteriormente he logrado hacer lo mismo al escribir mi propia HttpWebRequest. Sin embargo, solo funciona cuando obtengo manualmente la cookie de mi navegador e inserto una nueva cookie en la solicitud cada vez que hago una nueva "sesión". Ahora estoy tratando de usar BrowserSession para obtener una navegación más inteligente.

Aquí está el código actual:

BrowserSession b = new BrowserSession(); 

b.Get(@"http://www.facebook.com/login.php"); 
b.FormElements["email"] = "[email protected]"; 
b.FormElements["pass"] = "xxxxxxxx"; 
b.FormElements["lsd"] = "qDhIH"; 
b.FormElements["trynum"] = "1"; 
b.FormElements["persistent_inputcheckbox"] = "1"; 

var response = b.Post(@"https://login.facebook.com/login.php?login_attempt=1"); 

Lo anterior funciona bien. Surgen problemas cuando trato de usar esta BrowserSession nuevamente para buscar otra página. Lo estoy haciendo de esta manera, ya que BrowserSession guarda las cookies de la última respuesta y las inserta en la próxima solicitud, por lo que ya no debería tener que ingresar datos de cookie manualmente desde mi navegador.

Sin embargo, cuando trato de hacer algo como esto:

var profilePage = b.Get(@"https://m.facebook.com/profile.php?id=1111111111"); 

el doctor regrese está vacía. Agradecería cualquier comentario sobre lo que estoy haciendo mal.

Respuesta

9

Disculpe, no sé mucho sobre el paquete de agilidad HTML o la clase BrowserSession que ha mencionado. Pero probé el mismo escenario con HtmlUnit y funciona bien. Estoy usando un envoltorio NET (el código fuente de la que se puede encontrar here y se explica un poco más here), y aquí está el código que he usado (eliminado algunos detalles para proteger a los inocentes):

var driver = new HtmlUnitDriver(true); 
driver.Url = @"http://www.facebook.com/login.php"; 

var email = driver.FindElement(By.Name("email")); 
email.SendKeys("[email protected]"); 

var pass = driver.FindElement(By.Name("pass")); 
pass.SendKeys("xxxxxxxx"); 

var inputs = driver.FindElements(By.TagName("input")); 
var loginButton = (from input in inputs 
        where input.GetAttribute("value").ToLower() == "login" 
        && input.GetAttribute("type").ToLower() == "submit" 
        select input).First(); 
loginButton.Click(); 

driver.Url = @"https://m.facebook.com/profile.php?id=1111111111"; 
Assert.That(driver.Title, Is.StringContaining("Title of page goes here")); 

Espero que esto ayude.

+0

¡Gracias! Esta fue una buena solución y funcionó muy bien :) –

+0

Eres bienvenido. Buena suerte con su proyecto :) – Mhmmd

+0

pensé que esto era solo para Java, ¿se puede usar para .net – Smith

0

¿Has comprobado su nueva API? http://developers.facebook.com/docs/authentication/

Usted puede llamar a una URL sencilla de obtener un acceso OAuth2.0 ficha y adjuntar que en el resto de sus peticiones ...

https://graph.facebook.com/oauth/authorize? 
    client_id=...& 
    redirect_uri=http://www.example.com/oauth_redirect 

Cambio redirect_uri a cualquier URL que desea, y lo hará recibir una llamada de regreso con un parámetro llamado "access_token" en él. Obtenga eso y realice las llamadas de SDK automáticas que desee.

+0

Gracias por la respuesta. Solo estoy haciendo una investigación privada en gráficos sociales y solo necesito automatizar mi propia navegación a través de mis propios amigos, en lugar de guardar páginas manualmente. Probablemente tomaría mucho menos tiempo hacerlo manualmente, pero será más divertido automatizar :) No necesito o quiero y la aplicación de Facebook real. Además, la API no puede ver todo lo que puedo ver como usuario registrado, y de todos modos mi tarea actual a la que estoy atascado es aprender a usar BrowserSession correctamente. –

2

Es posible que desee utilizar WatiN (Web Application Testing In .Net) O Selenium para conducir su navegador. Esto le ayudará a asegurarse de no tener que manipular las cookies y hacer un trabajo personalizado para que las solicitudes subsiguientes funcionen, ya que está simulando usuarios reales.

+0

Gracias por la respuesta, he encontrado mi solución por ahora pero puedo volver a sus ejemplos más adelante :) –

1

Hoy estaba enfrentando el mismo problema. También trabajé con la clase BrowserSession de Rohit Agarwal junto con HtmlAgilityPack. Después de la programación de prueba y error durante todo el día, descubrí que el problema se debe a no haber configurado las cookies correctas en las solicitudes posteriores. No pude cambiar el código inicial de BrowserSession para que funcione correctamente, pero agregué las siguientes funciones y modifiqué ligeramente la función SameCookieFrom. Al final funcionó muy bien para mí.

Las funciones añadidas/modificados son los siguientes:

class BrowserSession{ 
    private bool _isPost; 
    private HtmlDocument _htmlDoc; 
    public CookieContainer cookiePot; //<- This is the new CookieContainer 

... 

    public string Get2(string url) 
    { 
     HtmlWeb web = new HtmlWeb(); 
     web.UseCookies = true; 
     web.PreRequest = new HtmlWeb.PreRequestHandler(OnPreRequest2); 
     web.PostResponse = new HtmlWeb.PostResponseHandler(OnAfterResponse2); 
     HtmlDocument doc = web.Load(url); 
     return doc.DocumentNode.InnerHtml; 
    } 
    public bool OnPreRequest2(HttpWebRequest request) 
    { 
     request.CookieContainer = cookiePot; 
     return true; 
    } 
    protected void OnAfterResponse2(HttpWebRequest request, HttpWebResponse response) 
    { 
     //do nothing 
    } 
    private void SaveCookiesFrom(HttpWebResponse response) 
    { 
     if ((response.Cookies.Count > 0)) 
     { 
      if (Cookies == null) 
      { 
       Cookies = new CookieCollection(); 
      }  
      Cookies.Add(response.Cookies); 
      cookiePot.Add(Cookies);  //-> add the Cookies to the cookiePot 
     } 
    } 

Lo que hace: Básicamente guarda las cookies de la "post-respuesta" inicial y añade el mismo CookieContainer a la petición de llamada más tarde. No entiendo completamente por qué no funcionaba en la versión inicial porque de alguna manera hace lo mismo en la función AddCookiesTo. (if (¡Cookies! = nulo & & Cookies.Count> 0) request.CookieContainer.Add (Cookies);) De todos modos, con estas funciones adicionales debería funcionar bien ahora.

Se puede utilizar la siguiente manera:

//initial "Login-procedure" 
BrowserSession b = new BrowserSession(); 
b.Get("http://www.blablubb/login.php"); 
b.FormElements["username"] = "yourusername"; 
b.FormElements["password"] = "yourpass"; 
string response = b.Post("http://www.blablubb/login.php"); 

todas las llamadas posteriores deben utilizar:

response = b.Get2("http://www.blablubb/secondpageyouwannabrowseto"); 
response = b.Get2("http://www.blablubb/thirdpageyouwannabrowseto"); 
... 

espero que ayuda a muchas personas que enfrentan el mismo problema!

12

He solucionado la causa raíz de esto si a alguien le importa. Resulta que las cookies se estaban guardando en el CookieContainer del objeto REQUEST y no en el objeto de respuesta. También agregué la capacidad de descargar un archivo (siempre que el archivo esté basado en cadenas). Código duda de que no es seguro para subprocesos, pero el objeto no era seguro para subprocesos, para empezar:

public class BrowserSession 
{ 
    private bool _isPost; 
    private bool _isDownload; 
    private HtmlDocument _htmlDoc; 
    private string _download; 

    /// <summary> 
    /// System.Net.CookieCollection. Provides a collection container for instances of Cookie class 
    /// </summary> 
    public CookieCollection Cookies { get; set; } 

    /// <summary> 
    /// Provide a key-value-pair collection of form elements 
    /// </summary> 
    public FormElementCollection FormElements { get; set; } 

    /// <summary> 
    /// Makes a HTTP GET request to the given URL 
    /// </summary> 
    public string Get(string url) 
    { 
     _isPost = false; 
     CreateWebRequestObject().Load(url); 
     return _htmlDoc.DocumentNode.InnerHtml; 
    } 

    /// <summary> 
    /// Makes a HTTP POST request to the given URL 
    /// </summary> 
    public string Post(string url) 
    { 
     _isPost = true; 
     CreateWebRequestObject().Load(url, "POST"); 
     return _htmlDoc.DocumentNode.InnerHtml; 
    } 

    public string GetDownload(string url) 
    { 
     _isPost = false; 
     _isDownload = true; 
     CreateWebRequestObject().Load(url); 
     return _download; 
    } 

    /// <summary> 
    /// Creates the HtmlWeb object and initializes all event handlers. 
    /// </summary> 
    private HtmlWeb CreateWebRequestObject() 
    { 
     HtmlWeb web = new HtmlWeb(); 
     web.UseCookies = true; 
     web.PreRequest = new HtmlWeb.PreRequestHandler(OnPreRequest); 
     web.PostResponse = new HtmlWeb.PostResponseHandler(OnAfterResponse); 
     web.PreHandleDocument = new HtmlWeb.PreHandleDocumentHandler(OnPreHandleDocument); 
     return web; 
    } 

    /// <summary> 
    /// Event handler for HtmlWeb.PreRequestHandler. Occurs before an HTTP request is executed. 
    /// </summary> 
    protected bool OnPreRequest(HttpWebRequest request) 
    { 
     AddCookiesTo(request);    // Add cookies that were saved from previous requests 
     if (_isPost) AddPostDataTo(request); // We only need to add post data on a POST request 
     return true; 
    } 

    /// <summary> 
    /// Event handler for HtmlWeb.PostResponseHandler. Occurs after a HTTP response is received 
    /// </summary> 
    protected void OnAfterResponse(HttpWebRequest request, HttpWebResponse response) 
    { 
     SaveCookiesFrom(request, response); // Save cookies for subsequent requests 

     if (response != null && _isDownload) 
     { 
      Stream remoteStream = response.GetResponseStream(); 
      var sr = new StreamReader(remoteStream); 
      _download = sr.ReadToEnd(); 
     } 
    } 

    /// <summary> 
    /// Event handler for HtmlWeb.PreHandleDocumentHandler. Occurs before a HTML document is handled 
    /// </summary> 
    protected void OnPreHandleDocument(HtmlDocument document) 
    { 
     SaveHtmlDocument(document); 
    } 

    /// <summary> 
    /// Assembles the Post data and attaches to the request object 
    /// </summary> 
    private void AddPostDataTo(HttpWebRequest request) 
    { 
     string payload = FormElements.AssemblePostPayload(); 
     byte[] buff = Encoding.UTF8.GetBytes(payload.ToCharArray()); 
     request.ContentLength = buff.Length; 
     request.ContentType = "application/x-www-form-urlencoded"; 
     System.IO.Stream reqStream = request.GetRequestStream(); 
     reqStream.Write(buff, 0, buff.Length); 
    } 

    /// <summary> 
    /// Add cookies to the request object 
    /// </summary> 
    private void AddCookiesTo(HttpWebRequest request) 
    { 
     if (Cookies != null && Cookies.Count > 0) 
     { 
      request.CookieContainer.Add(Cookies); 
     } 
    } 

    /// <summary> 
    /// Saves cookies from the response object to the local CookieCollection object 
    /// </summary> 
    private void SaveCookiesFrom(HttpWebRequest request, HttpWebResponse response) 
    { 
     //save the cookies ;) 
     if (request.CookieContainer.Count > 0 || response.Cookies.Count > 0) 
     { 
      if (Cookies == null) 
      { 
       Cookies = new CookieCollection(); 
      } 

      Cookies.Add(request.CookieContainer.GetCookies(request.RequestUri)); 
      Cookies.Add(response.Cookies); 
     } 
    } 

    /// <summary> 
    /// Saves the form elements collection by parsing the HTML document 
    /// </summary> 
    private void SaveHtmlDocument(HtmlDocument document) 
    { 
     _htmlDoc = document; 
     FormElements = new FormElementCollection(_htmlDoc); 
    } 
} 

/// <summary> 
/// Represents a combined list and collection of Form Elements. 
/// </summary> 
public class FormElementCollection : Dictionary<string, string> 
{ 
    /// <summary> 
    /// Constructor. Parses the HtmlDocument to get all form input elements. 
    /// </summary> 
    public FormElementCollection(HtmlDocument htmlDoc) 
    { 
     var inputs = htmlDoc.DocumentNode.Descendants("input"); 
     foreach (var element in inputs) 
     { 
      string name = element.GetAttributeValue("name", "undefined"); 
      string value = element.GetAttributeValue("value", ""); 

      if (!this.ContainsKey(name)) 
      { 
       if (!name.Equals("undefined")) 
       { 
        Add(name, value); 
       } 
      } 
     } 
    } 

    /// <summary> 
    /// Assembles all form elements and values to POST. Also html encodes the values. 
    /// </summary> 
    public string AssemblePostPayload() 
    { 
     StringBuilder sb = new StringBuilder(); 
     foreach (var element in this) 
     { 
      string value = System.Web.HttpUtility.UrlEncode(element.Value); 
      sb.Append("&" + element.Key + "=" + value); 
     } 
     return sb.ToString().Substring(1); 
    } 
} 
2

tenía síntomas similares - inicio de sesión, pero trabajó cookie de autenticación no estaba presente en el recipiente de horno y así que no fue enviado en solicitudes posteriores. Descubrí que esto se debía a que la solicitud web manejaba el encabezado Ubicación: internamente, redirigiendo detrás de escena a una nueva página, perdiendo las cookies en el proceso. Lo arreglé agregando:

request.AllowAutoRedirect = false; // Location header messing up cookie handling! 

... a la función OnPreRequest(). Ahora se ve así:

protected bool OnPreRequest(HttpWebRequest request) 
    { 
     request.AllowAutoRedirect = false; // Location header messing up cookie handling! 

     AddCookiesTo(request);    // Add cookies that were saved from previous requests 
     if (_isPost) AddPostDataTo(request); // We only need to add post data on a POST request 
     return true; 
    } 

Espero que esto pueda ayudar a alguien que tenga el mismo problema.

Cuestiones relacionadas