【问题标题】:Abot Crawler Omit CrawledPage HttpWebRequest/ResponseAbot Crawler 省略 CrawledPage HttpWebRequest/Response
【发布时间】:2015-04-03 14:46:54
【问题描述】:

我使用 Abot 的方式是,我有一个 WPF 应用程序,它显示一个浏览器控件 (CefSharp)。 用户登录并且网站使用的任何可能的自定义身份验证都将在抓取时以与用户实际浏览网站相同的方式工作。

因此,当我抓取时,我想使用此浏览器控件发出请求并简单地返回页面数据。 因此,我实现了我的自定义 PageRequester,完整的清单如下。

问题在于,对于 CefSharp,与其他浏览器控件一样,无法获取与 CrawlPage 关联的 HttpWebRequest/Response。 如果不设置这两个属性,Abot 就不会继续爬取。

我可以做些什么来规避这个问题吗?

代码清单:

using Abot.Core;
using Abot.Poco;
using CefSharp.Wpf;
using System;
using System.Net;
using System.Text;
using System.Threading;

public class CefPageRequester : IPageRequester
{
    private MainWindowDataContext DataContext;
    private ChromiumWebBrowser ChromiumWebBrowser;
    private CrawlConfiguration CrawlConfig;

    private volatile bool _navigationCompleted;
    private string _pageSource;

    public CefPageRequester(MainWindowDataContext dataContext, ChromiumWebBrowser chromiumWebBrowser, CrawlConfiguration crawlConfig)
    {
        this.DataContext = dataContext;
        this.ChromiumWebBrowser = chromiumWebBrowser;
        this.CrawlConfig = crawlConfig;

        this.ChromiumWebBrowser.FrameLoadEnd += ChromiumWebBrowser_FrameLoadEnd;
    }

    public CrawledPage MakeRequest(Uri uri)
    {
        return this.MakeRequest(uri, cp => new CrawlDecision() { Allow = true });
    }

    public CrawledPage MakeRequest(Uri uri, Func<CrawledPage, CrawlDecision> shouldDownloadContent)
    {
        if (uri == null)
            throw new ArgumentNullException("uri");

        CrawledPage crawledPage = new CrawledPage(uri);

        try
        {
            //the browser control is bound to the address of the data context, 
            //if we set the address directly it breaks for some reason, although it's a two way binding.
            this.DataContext.Address = uri.AbsolutePath;

            crawledPage.RequestStarted = DateTime.Now;
            crawledPage.DownloadContentStarted = crawledPage.RequestStarted;

            while (!_navigationCompleted)
                Thread.CurrentThread.Join(10);
        }
        catch (WebException e)
        {
            crawledPage.WebException = e;
        }
        catch
        {
            //bad luck, we should log this.
        }
        finally
        {
            //TODO must add these properties!!
            //crawledPage.HttpWebRequest = request;
            //crawledPage.HttpWebResponse = response;
            crawledPage.RequestCompleted = DateTime.Now;
            crawledPage.DownloadContentCompleted = crawledPage.RequestCompleted;
            if (!String.IsNullOrWhiteSpace(_pageSource))
                crawledPage.Content = this.GetContent("UTF-8", _pageSource);

            _navigationCompleted = false;
            _pageSource = null;
        }

        return crawledPage;
    }

    private void ChromiumWebBrowser_FrameLoadEnd(object sender, CefSharp.FrameLoadEndEventArgs e)
    {
        if (!e.IsMainFrame)
            return;

        this.ChromiumWebBrowser.Dispatcher.BeginInvoke(
            (Action)(() =>
            {
                _pageSource = this.ChromiumWebBrowser.GetSourceAsync().Result;
                _navigationCompleted = true;
            }));
    }

    private PageContent GetContent(string charset, string html)
    {
        PageContent pageContent = new PageContent();
        pageContent.Charset = charset;
        pageContent.Encoding = this.GetEncoding(charset);
        pageContent.Text = html;
        pageContent.Bytes = pageContent.Encoding.GetBytes(html);

        return pageContent;
    }

    private Encoding GetEncoding(string charset)
    {
        Encoding e = Encoding.UTF8;
        if (charset != null)
        {
            try
            {
                e = Encoding.GetEncoding(charset);
            }
            catch { }
        }

        return e;
    }
}

这个问题也可以表述为:如何避免必须从流中创建 HttpWebResponse?鉴于 MSDN says,这似乎是不可能的:

您永远不应该直接创建 HttpWebResponse 的实例 班级。相反,使用调用返回的实例 HttpWebRequest.GetResponse。

我必须实际发布请求才能获得响应,而这正是我想要通过使用 Web 浏览器控件来避免的。

【问题讨论】:

    标签: c# web-crawler httprequest httpresponse cefsharp


    【解决方案1】:

    如您所知,许多功能取决于设置的 HttpWebRequest 和 HttpWebResponse。我已经为您订购了一些选项...

    1) 重构 Abot 以使用一些 POCO 抽象而不是那些类。然后只需有一个转换器将真正的 HttpWebRequest 和 HttpWebResponse 转换为这些 POCO 类型,以及一个转换器将您的浏览器对象响应转换为这些 POCO。

    2) 创建一个继承自 .net 类的 CustomHttpWebRequest 和 CustomHttpWebResponse,以便您可以访问/覆盖公共/受保护的属性,这可能允许您手动创建一个实例来模拟您的浏览器组件返回给您的请求/响应.我知道这可能很棘手,但可能会奏效(我从来没有做过,所以我不能肯定地说)。

    3) [我讨厌这个主意。它应该是您最后的手段] 创建这些类的真实实例并使用反射来设置需要设置的任何属性/值以满足 Abot 的所有用途。

    4) [我更讨厌这个想法] 使用 MS Fakes 为 HttpWebRequest 和 HttpWebResponse 的属性和方法创建 shims/stubs/fakes。然后你可以配置它来返回你的值。此工具通常仅用于测试,但我相信如果您不顾一切、不关心性能和/或精神错乱,它可以用于生产代码。

    我还包括了糟糕的想法,以防它们帮助您激发一些想法。希望对您有所帮助...

    【讨论】:

    • 所以,重构比我想象的要简单,我可以在问之前研究一下。 2是不可能的,我没有往下走。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-13
    • 1970-01-01
    • 1970-01-01
    • 2011-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多