Abot Crawler Omit CrawledPage HttpWebRequest / Response

1

Я использую Abot таким образом, что у меня есть приложение WPF, которое отображает элемент управления браузера (CefSharp). Пользователь регистрируется и, в зависимости от того, какая возможная пользовательская аутентификация, которую использует сайт, будет работать во время сканирования таким же образом, как если бы пользователь действительно просматривал сайт.

Таким образом, когда я сканирую, я хочу использовать этот элемент управления браузера, чтобы выполнить запрос и просто вернуть данные страницы. Поэтому я внедрил свой собственный PageRequester, полный список ниже.

Проблема в том, что с CefSharp, как и с другими элементами управления браузера, невозможно получить HttpWebRequest/Response, связанный с CrawlPage. Не устанавливая эти два свойства, Абот не продолжит сканирование дальше.

Есть ли что-то, что я могу сделать, чтобы обойти эту проблему?

Список кодов:

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 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 говорит:

Вы никогда не должны напрямую создавать экземпляр класса HttpWebResponse. Вместо этого используйте экземпляр, возвращенный вызовом HttpWebRequest.GetResponse.

Я должен был бы опубликовать запрос, чтобы получить ответ, чего я хочу избежать, имея управление веб-браузером.

Теги:
web-crawler
httprequest
httpresponse
cefsharp

1 ответ

1
Лучший ответ

Как вам известно, многие функции зависят от установленного HttpWebRequest и HttpWebResponse. Я заказал несколько вариантов для вас с головы до головы...

1) Refactor Abot использовать некоторую абстракцию POCO вместо этих классов. Затем просто конвертер, который преобразует реальные HttpWebRequest и HttpWebResponse в эти типы POCO, а также конвертер, который преобразует ваш ответ объектов браузера в эти POCOs.

2) Создайте CustomHttpWebRequest и CustomHttpWebResponse, которые наследуются от классов.net, чтобы вы могли получить доступ/переопределить общедоступные/защищенные свойства, которые могут позволить вам вручную создать экземпляр, который моделирует запрос/ответ, возвращаемый вашим компонентом браузера. Я знаю, что это может быть сложно, но может работать (я никогда не делал этого, поэтому не могу сказать точно).

3) [НЕНАВИЖУ ЭТУ ИДЕЮ. Это ДОЛЖНО БЫТЬ ВАШИМ ПОСЛЕДНИМ КУРОРТОМ] Создайте реальный экземпляр этих классов и используйте отражение, чтобы установить, какие свойства/значения нужно установить, чтобы удовлетворить все обычаи Abot.

4) [Я НЕНАВИЖУ ЭТУ ИДЕЮ ДАЖЕ ВИДЕО] Используйте MS Fakes для создания прокладок/заглушек/подделок для свойств и методов HttpWebRequest и HttpWebResponse. Затем вы можете настроить его для возврата ваших значений. Этот инструмент обычно используется только для тестирования, но я считаю, что его можно использовать для производственного кода, если вы в отчаянии, не заботитесь о производительности и/или безумны.

Я также включил ужасные идеи, а на случай, если они помогут вам задуматься. Надеюсь, это поможет...

  • 0
    Таким образом, рефакторинг был проще, чем я предполагал, я мог бы рассмотреть это, прежде чем спрашивать. 2 невозможно, и я не пошел ниже.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню