使用WebBrowser自动下载文件而无需URL

9
我一直在使用C#和System.Windows.Forms.WebBrowser编写WebCrawler。我正在尝试从网站下载文件并保存到本地机器上,更重要的是,我希望这是完全自动化的。文件下载可以通过单击调用JavaScript函数来启动,显示“您要打开还是保存此文件?”对话框。我绝对不想手动点击“另存为”并输入文件名。
我知道HttpWebRequest和WebClient的下载功能,但由于下载是通过JavaScript启动的,因此我不知道文件的URL。顺便说一下,JavaScript是一个doPostBack函数,它会更改一些值并提交表单。
我尝试从WebBrowser获取保存对话框的焦点以便从那里自动化它,但没有太大成功。我知道有一种方法可以通过添加http请求头来强制下载保存而不是询问是否保存或打开,但我不知道如何指定要下载到的文件路径。

你有解决上一个问题的方案吗?当文件是动态生成的,而且你无法确定它是从URL下载的文件时,如何下载该文件? - user741255
4个回答

6
我认为你应该防止下载对话框的出现。以下是一种可能的方法:
  • JavaScript代码导致WebBrowser控件导航到特定URL(这会导致下载对话框出现)。

  • 为了防止WebBrowser控件实际导航到此URL,请将事件处理程序附加到Navigating事件。

  • 在Navigating事件中,您需要分析是否停止此实际导航操作(这是否为下载URL,也许检查文件扩展名,必须有可识别的格式)。使用WebBrowserNavigatingEventArgs.Url进行分析。

  • 如果这是正确的URL,请通过设置WebBrowserNavigatingEventArgs.Cancel属性来停止导航。

  • 使用HttpWebRequest或WebClient类自己继续下载。

请参阅此页面以获取有关事件的更多信息:
http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowser.navigating.aspx


1
我已经尝试使用HttpDebugger获取URL以查看HTTP请求和响应。 URL完全相同,一个是GET请求,另一个是POST请求。我也尝试了你的建议,但没有成功。 - Sharath
你可能想使用WebBrowser控件来到达最后,就在表单提交之前,然后使用DOM提取表单的POST目标(获取对HTML文档主体的引用,然后从那里进入表单)。 - Yvo

6

4

2
虽然这理论上回答了问题,但最好在此处包含答案的基本部分,并提供参考链接。 - Jérémie Bertrand

3
假设使用System.Windows.Forms.WebBrowser访问了一个受保护的页面,并且要下载其中的受保护链接:
以下代码使用Web浏览器检索要下载的实际链接。此代码需要根据您的具体操作进行更改。 重要部分是字段documentLinkUrl,将在下面使用。
var documentLinkUrl = default(Uri);
browser.DocumentCompleted += (object sender, WebBrowserDocumentCompletedEventArgs e) =>
{
    var aspForm = browser.Document.Forms[0];
    var downloadLink = browser.Document.ActiveElement
        .GetElementsByTagName("a").OfType<HtmlElement>()
        .Where(atag => 
            atag.GetAttribute("href").Contains("DownloadAttachment.aspx"))
        .First();

    var documentLinkString = downloadLink.GetAttribute("href");
   documentLinkUrl = new Uri(documentLinkString);
}
browser.Navigate(yourProtectedPage);

现在,当浏览器导航到受保护的页面并获取下载链接后,此代码将下载该链接。

private static async Task DownloadLinkAsync(Uri documentLinkUrl)
{
    var cookieString = GetGlobalCookies(documentLinkUrl.AbsoluteUri);
    var cookieContainer = new CookieContainer();
    using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
    using (var client = new HttpClient(handler) { BaseAddress = documentLinkUrl })
    {
        cookieContainer.SetCookies(this.documentLinkUrl, cookieString);
        var response = await client.GetAsync(documentLinkUrl);
        if (response.IsSuccessStatusCode)
        {
            var responseAsString = await response.Content.ReadAsStreamAsync();
            // Response can be saved from Stream

        }
    }
}

上面的代码依赖于 Erika Chinchio 的 GetGlobalCookies 方法,该方法可以在 @Pedro Leonardo 提供的优秀文章中找到(可在此处查看)。
[System.Runtime.InteropServices.DllImport("wininet.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
static extern bool InternetGetCookieEx(string pchURL, string pchCookieName,
    System.Text.StringBuilder pchCookieData, ref uint pcchCookieData, int dwFlags, IntPtr lpReserved);

const int INTERNET_COOKIE_HTTPONLY = 0x00002000;

private string GetGlobalCookies(string uri)
{
    uint uiDataSize = 2048;
    var sbCookieData = new System.Text.StringBuilder((int)uiDataSize);
    if (InternetGetCookieEx(uri, null, sbCookieData, ref uiDataSize,
            INTERNET_COOKIE_HTTPONLY, IntPtr.Zero)
        &&
        sbCookieData.Length > 0)
    {
        return sbCookieData.ToString().Replace(";", ",");
    }
    return null;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接