Uso de BrowserSession y HtmlAgilityPack para iniciar sesión en Facebook a través de .NET

.net c# cookies html-agility-pack

Pregunta

Estoy intentando usar la clase BrowserSession de Rohit Agarwal junto con HtmlAgilityPack para iniciar sesión y posteriormente navegar por Facebook.

Anteriormente logré hacer lo mismo escribiendo mis propios HttpWebRequest's. Sin embargo, solo funciona cuando obtengo manualmente la cookie de mi navegador e inserto una nueva cadena de cookies 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"] = "some@email.com";
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. El problema viene 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 siguiente solicitud, por lo que no debería tener que insertar manualmente los cookiedata obtenidos desde mi navegador.

Sin embargo, cuando intento hacer algo como esto:

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

El documento que recupero está vacío. Apreciaría cualquier entrada sobre lo que estoy haciendo mal.

Respuesta aceptada

Lo sentimos, no sé mucho sobre el paquete de agilidad HTML o la clase BrowserSession que ha mencionado. Pero probé el mismo escenario con HtmlUnit y funcionó bien. Estoy usando un envoltorio .NET (el código fuente del cual se puede encontrar aquí y se explica un poco más aquí ), y aquí está el código que he usado (algunos detalles eliminados 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("some@email.com");

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.


Respuesta popular

Arreglé 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). El código definitivamente 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);
    }
}



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué