Gibt es trotzdem "BrowserSession" um Dateien herunterzuladen? C #

c# cookies download html-agility-pack

Frage

Ich habe eine Website, die eine Anmeldung erfordert, bevor Sie Dateien herunterladen können. Zurzeit benutze ich die BrowserSession- Klasse, um mich einzuloggen und alle erforderlichen Scraping- Vorgänge durchzuführen (zumindest größtenteils).

Quelle der BrowserSession-Klasse am Ende des Posts:

Die Download-Links werden auf den Dokumentknoten angezeigt. Aber ich weiß nicht, wie ich die Download-Funktionalität zu dieser Klasse hinzufügen kann, und wenn ich versuche, sie mit einem Webclient herunterzuladen, muss ich die BrowserSession-Klasse bereits stark modifizieren (ich hätte sie als Teilweise modifizieren müssen, aber nicht t) Ich möchte nicht wirklich von der BrowserSession-Klasse abweichen.

Ich glaube, dass es htmlAgilityPack.HtmlWeb verwendet, um die Webseiten herunterzuladen und zu laden.

Wenn es keine einfache Möglichkeit gibt, die BrowserSession zu ändern, gibt es eine Möglichkeit, CookieCollection mit Webclient zu verwenden?

PS: Ich muss eingeloggt sein, um die Datei herunterladen zu können, ansonsten wird der Link zum Login-Bildschirm weitergeleitet. Aus diesem Grund kann ich WebClient nicht einfach verwenden und muss entweder die BrowserSession-Klasse ändern, um WebClient herunterladen oder ändern zu können, um Cookies zu verwenden, bevor eine Seite abgerufen wird.

Ich gebe zu, dass ich Cookies nicht gut verstehe (ich bin mir nicht sicher, ob sie jedes Mal benutzt werden, wenn GET benutzt wird, oder wenn es nur auf POST ist), aber bis jetzt hat BrowserSession sich darum gekümmert.

PPS: Die BrowserSession, die ich gepostet habe, ist nicht die, die ich auch hinzugefügt habe, aber die Kernfunktionen sind alle gleich.

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

FormElementCollection-Klasse:

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

Akzeptierte Antwort

Ich habe es mit BrowserSession und einem modifizierten webClient geschafft:

Als Erstes ändern Sie _htmlDoc in Public, um auf die Dokumentknoten zuzugreifen:

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

Zweitens Fügen Sie diese Methode zu BrowserSession hinzu:

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

Third Fügen Sie diese Klasse Somewhere hinzu, wodurch die Cookies von BrowserSession an den WebClient übergeben werden können.

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

Dies sollte Ihnen die Möglichkeit geben, BrowserSession wie gewohnt zu verwenden, und wenn Sie eine Datei benötigen, auf die Sie nur zugreifen können. Wenn Sie angemeldet sind, rufen Sie einfach BrowserSession.DownloadCookieProtectedFile () auf Als wäre es ein WebClient, setzen Sie nur die Cookies so:

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

Beliebte Antwort

Es ist nicht einfach, sich einzuloggen und die WebPages herunterzuladen. Ich habe in letzter Zeit das gleiche Problem gehabt. Wenn Sie eine Lösung von diesem Problem finden, geben Sie es bitte an.

Jetzt habe ich Selenium mit PhantomJS benutzt. Mit Selenium kann ich mit dem Webbrowser meiner Wahl interagieren.

Auch die Browser-Klasse verwendet nicht das Html Agility Pack, das eine Drittanbieter-Bibliothek ist, die über nuget verfügbar ist.

Ich möchte Sie auf diese Frage verweisen, wo ich ein komplettes Beispiel für die Verwendung von Selenium erstellt habe und wie Sie das HtmlDocument herunterladen und die notwendigen Informationen mit xpath ausfiltern können.



Related

Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow