¿Hay alguna forma de usar "BrowserSession" para descargar archivos? DO#

c# cookies download html-agility-pack

Pregunta

Tengo un sitio que requiere inicio de sesión antes de que te permita descargar archivos. Actualmente estoy usando la clase BrowserSession para iniciar sesión y hacer todo el raspado requerido (al menos en su mayor parte).

Fuente de BrowserSession Class al final del post:

Los enlaces de descarga aparecen en los nodos del documento. Pero no sé cómo agregar la funcionalidad de descarga a esa clase, y si intento descargarlos con un cliente web, falla, ya tuve que modificar en gran medida la clase BrowserSession (debería haberlo modificado como parcial pero no lo hice t) Así que realmente no quiero cambiar de usar la Clase BrowserSession.

Creo que está usando htmlAgilityPack.HtmlWeb para descargar y cargar las páginas web.

Si no hay una manera fácil de modificar BrowserSession, ¿hay alguna forma de usar CookieCollection With Webclient?

PS: Necesito iniciar sesión para descargar el archivo, de lo contrario, el enlace redirige a la pantalla de inicio de sesión. Es por eso que no puedo simplemente usar WebClient, y necesito modificar la clase BrowserSession para poder descargar, o modificar WebClient para usar cookies antes de obtener una página.

Admito que no entiendo muy bien las cookies (no estoy seguro de si se usan cada vez que se usa GET, o si es solo en POST), pero hasta ahora BrowserSession se ha ocupado de todo eso.

PPS: La BrowserSession que publiqué no es la que agregué cosas también, sin embargo, las funciones principales son todas iguales.

public class BrowserSession
{
private bool _isPost;
private HtmlDocument _htmlDoc;

/// <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;
}

/// <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(response); // Save cookies for subsequent requests
}

/// <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(HttpWebResponse response)
{
    if (response.Cookies.Count > 0)
    {
        if (Cookies == null)  Cookies = new CookieCollection(); 
        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);
}
}

Clase FormElementCollection:

/// <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 (!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);
}
}

Respuesta aceptada

Logré que funcionara, usando BrowserSession y un cliente web modificado:

En primer lugar, cambie el _htmlDoc a Público para acceder a los Nodos del documento:

public class BrowserSession
{
    private bool _isPost;
    public string previous_Response { get; private set; }
    public HtmlDocument _htmlDoc { get; private set; }
}

En segundo lugar, agregue este método a BrowserSession:

 public void DownloadCookieProtectedFile(string url, string Filename)
    {
        using (CookieAwareWebClient wc = new CookieAwareWebClient())
        {
            wc.Cookies = Cookies;
            wc.DownloadFile(url, Filename);
        }
    }
//rest of BrowserSession

En tercer lugar, agregue esta clase en algún lugar, que permita pasar las cookies de BrowserSession al cliente web.

public class CookieAwareWebClient : WebClient
{
    public CookieCollection Cookies = new CookieCollection();
    private void AddCookiesTo(HttpWebRequest request)
    {
        if (Cookies != null && Cookies.Count > 0)
        {
            request.CookieContainer.Add(Cookies);
        }
    }

    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest request = base.GetWebRequest(address);
        HttpWebRequest webRequest = request as HttpWebRequest;
        if (webRequest != null)
        {
            if (webRequest.CookieContainer == null) webRequest.CookieContainer = new CookieContainer();
            AddCookiesTo(webRequest);
        }
        return request;
    }
}

Esto debería darle la posibilidad de usar BrowserSession como lo haría normalmente, y cuando necesite obtener un archivo al que solo pueda acceder. Si inició sesión, simplemente llame BrowserSession.DownloadCookieProtectedFile () como si fuera un cliente web, solo configure las cookies al igual que:

Using(wc = new CookieAwareWebClient())
{
    wc.Cookies = BrowserSession.Cookies
    //Download with WebClient As normal
    wc.DownloadFile();
}

Respuesta popular

No es fácil iniciar sesión y descargar las páginas web. Recientemente he tenido el mismo problema. Si encuentra una solución a partir de esta, por favor provéala.

Ahora lo que hice fue usar Selenium con PhantomJS. Con Selenium puedo interactuar con el navegador web de mi elección.

Además, la clase del navegador no usa Html Agility Pack, que es una biblioteca de terceros disponible a través de nuget.

Quiero referirme a esta pregunta , donde he creado un ejemplo completo de cómo usar Selenium y cómo descargar el documento Html y filtrar la información necesaria con xpath.



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é