Esiste comunque l'opzione "BrowserSession" per scaricare i file? C #

c# cookies download html-agility-pack

Domanda

Ho un sito che richiede il login prima che ti permetta di scaricare i file. Attualmente sto usando la classe BrowserSession per accedere e fare tutti gli scraping richiesti (almeno per la maggior parte).

Origine della classe BrowserSession nella parte inferiore del post:

I collegamenti di download vengono visualizzati nei nodi del documento. Ma non so come aggiungere funzionalità di download a quella classe, e se provo a scaricarli con un webclient fallisce, ho già dovuto modificare pesantemente la classe BrowserSession, (avrei dovuto modificarlo come parziale ma non lo avevo t) Quindi non voglio davvero passare dall'uso della classe BrowserSession.

Credo che utilizzi htmlAgilityPack.HtmlWeb per scaricare e caricare le pagine web.

Se non esiste un modo semplice per modificare BrowserSession, è possibile utilizzarlo con CookieCollection With Webclient?

PS: I È necessario effettuare il login per scaricare il file, altrimenti il ​​link reindirizza alla schermata di accesso. Ecco perché non riesco a utilizzare semplicemente WebClient e devo modificare la classe BrowserSession per poter scaricare o modificare WebClient per utilizzare i cookie prima di ottenere una pagina.

Devo ammettere che non capisco i cookie molto bene (non sono sicuro che vengano utilizzati ogni volta che viene utilizzato GET, o se è solo su POST), ma finora BrowserSession si è occupato di tutto ciò.

PPS: Il BrowserSession che ho pubblicato non è quello che ho aggiunto anche io, tuttavia le funzioni principali sono tutte uguali.

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

Classe 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);
}
}

Risposta accettata

Sono riuscito a farlo funzionare, utilizzando BrowserSession e un WebClient modificato:

Prima di tutto Modifica _htmlDoc su Pubblico per accedere ai nodi del documento:

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

In secondo luogo Aggiungi questo metodo a BrowserSession:

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

Terzo Aggiungi questa classe da qualche parte, che consente di passare i cookie da BrowserSession al WebClient.

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

Questo dovrebbe darti la possibilità di usare BrowserSession come faresti normalmente, e quando hai bisogno di ottenere un file a cui puoi accedere solo se hai effettuato l'accesso, chiama semplicemente BrowserSession.DownloadCookieProtectedFile () come se fosse un client Web, imposta solo i cookie così:

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

Risposta popolare

Non è facile accedere e scaricare le pagine Web. Recentemente ho avuto lo stesso problema. Se trovi una soluzione aisde da questa, ti preghiamo di fornirla.

Ora quello che ho fatto è stato usare Selenium con PhantomJS. Con il selenio posso interagire con il browser della mia scelta.

Anche la classe Browser non utilizza Html Agility Pack, che è una libreria di terze parti disponibile tramite nuget.

Voglio farvi riferimento a questa domanda , in cui ho creato un intero esempio di come utilizzare Selenium e come scaricare HtmlDocument e filtrare le informazioni necessarie usando xpath.



Related

Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché