【问题标题】:Is there anyway to use "BrowserSession" to download files? C#无论如何使用“BrowserSession”来下载文件? C#
【发布时间】:2015-11-12 19:36:57
【问题描述】:

我有一个网站需要先登录才能让您下载文件。目前我正在使用BrowserSession 类登录并执行所有需要的抓取(至少在大多数情况下)。

BrowserSession 类源在文章底部:

下载链接显示在文档节点上。但我不知道如何向该类添加下载功能,如果我尝试使用 webclient 下载它们失败,我已经不得不大量修改 BrowserSession 类,(我应该将它修改为 Partial 但没有t) 所以我真的不想改变使用 BrowserSession 类。

我相信它使用 htmlAgilityPack.HtmlWeb 来下载和加载网页。

如果没有简单的方法来修改BrowserSession,有没有办法使用它的CookieCollection With Webclient?

PS:我需要登录才能下载文件,否则链接会重定向到登录屏幕。这就是为什么我不能简单地使用 WebClient,并且要么需要修改 BrowserSession 类才能下载,要么修改 WebClient 以在获取页面之前使用 cookie。

我承认我不太了解 cookie(我不确定是每次使用 GET 时都使用它们,还是只在 POST 上使用它们),但到目前为止 BrowserSession 已经解决了所有这些问题。

PPS:我贴的BrowserSession也不是我加的,但是核心功能都是一样的。

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 类:

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

【问题讨论】:

    标签: c# cookies download html-agility-pack


    【解决方案1】:

    登录和下载网页并不容易。我最近遇到了同样的问题。如果您从该解决方案中找到解决方案,请提供。

    现在我所做的是将 Selenium 与 PhantomJS 一起使用。使用 Selenium,我可以与我选择的网络浏览器进行交互。

    Browser 类也不使用 Html Agility Pack,它是通过 nuget 提供的第三方库。

    我想向您推荐这个question,在那里我创建了一个完整的示例,说明如何使用 Selenium 以及如何下载 HtmlDocument 并使用 xpath 过滤出必要的信息。

    【讨论】:

    • 浏览器类不使用 Html Agility Pack,但BrowserSession 使用(我在上面发布的类)。我也确实设法让这个工作但我无法提供有用的答案,因为它有点令人困惑。稍后我会提出我最终确定的解决方案。
    • 我添加了我想出的解决方案。它不是很好,但它工作得相当好。 (到目前为止我还没有遇到任何问题)虽然我只有一个 BrowserSession 实例化。
    【解决方案2】:

    我设法让它工作,使用 BrowserSession 和修改后的 webClient:

    首先将 _htmlDoc 更改为 Public 以访问文档节点:

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

    再把这个方法加入BrowserSession:

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

    第三次在某处添加这个类,它允许将 cookie 从 BrowserSession 传递到 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;
        }
    }
    

    这应该使您能够像往常一样使用 BrowserSession,并且当您需要获取只能访问的文件时,如果您已登录,只需调用 BrowserSession.DownloadCookieProtectedFile() 就好像它是一个 WebClient 一样,只有像这样设置 Cookie:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-16
      • 2012-02-13
      • 2016-03-01
      • 2010-10-23
      相关资源
      最近更新 更多