пятница, 30 августа 2013 г.

Преобразование html в обычный текст при помощи элемента управления WebBrowser. C#, WinForms.

[Convert html to plain text using the WebBrowser control. C#, WinForms]

Для преобразования html в обычный текст без форматирования (также известный среди разработчиков как plain text), можно использовать много различных способов, от “ручного” парсинга html-тегов, до использования специальных библиотек для работы с html (самая известная среди них, пожалуй, “Html Agility Pack”). В интернете можно найти очень много примеров, одним из самых интересных способов, является на мой взгляд, применение xslt, где шаблон как фильтр исключает из результата ненужные теги.

Мои исходные данные были в бинарном формате (массив байтов: byte[]), причем данные были представлены в самых различных кодировках. Понятно, что основной проблемой становилось преобразование байтов в строку с учетом правильной кодировки. Вычитывать для этого charcterset “вручную” показалось мне слишко хлопотоным, поэтому я решил использовать элемент управления WebBrowser. Этот контрол базируется на установленном в системе Internet Explorer и справляется с черной работой за нас довольно таки неплохо.

Итак, все что нужно, это загрузить данные в элемент управления и получить значение свойства WebBrowser.Document.Body.InnerText.

Но и здесь, в таком простом сценарии, не обошлось без сюрпризов, с которыми мне пришлось некоторое время повозиться. Дело в том, что после установки источника данных, нельзя сразу обращаться к свойство Document.Body.InnerText, т.к. данные могут быть еще не загруженными, прежде нужно дождаться события DocumentCompleted. В следующем примере учитывается эта особенность. Обратите внимание на вызовы “Application.DoEvents()”, без них вызов события происходить не будет, программа зависает.

Также, хочу отметить, что при обработке значительной коллекции необходимо следить за своевременным освобождением ресурсов (использование Dispose или как здесь через using). Если этого не делать, память утекает, и программа падает. Так было у меня.


private String HtmlToPlainText(byte[] htmlBytes) {
    using (MemoryStream memStream = new MemoryStream(htmlBytes)) {
        memStream.Position = 0;

        using (WebBrowser webBrowser = new WebBrowser()) {
            webBrowser.ScriptErrorsSuppressed = true;

            bool loaded = false;
            webBrowser.DocumentCompleted += (x, y) => {
                loaded = true;
            };

            webBrowser.DocumentStream = memStream;

            // wait DocumentCompleted
            int waitCount = 0;
            Application.DoEvents(); // !!!
            while (loaded == false) {
                Thread.Sleep(200);
                Application.DoEvents(); // !!!
                waitCount++;
                if (waitCount * 50 >= 10000 /*ca. 10 sec*/ )
                    throw new Exception("timeout");
            }

            return webBrowser.Document.Body.InnerText;
        }
    }
}

Комментариев нет: