Синтаксический анализ AngleSharp для корректного HTML ‹script› и XML

Пишу парсер для сайтов с помощью AngleSharp, нужно в итоге получить XML.

При разборе в xml возникают проблемы с разбором скрипта раздела, т.к. там такие символы "‹%=" "%>". Ошибки возникают в строках, которые пронумерованы

Как я могу решить эту ситуацию (мне нужно использовать AngleSharp)? Пробовал с разных сайтов, результат тот же

        var config = Configuration.Default
                      .WithCss()
                      .WithDefaultLoader();
        var address = Url.Create("https://www.google.com/");
        var document = BrowsingContext.New(config).OpenAsync(address).GetAwaiter().GetResult();

        XmlDocument xmlDocument = new XmlDocument();
        var xDocument = new HtmlParser().Parse(document.DocumentElement.InnerHtml);
        var formatter = new AngleSharp.Xml.XmlMarkupFormatter();
        var result = xDocument.ToHtml(formatter);
        xmlDocument.LoadXml(result); //1

        var parserXML = new XmlParser().Parse(document.DocumentElement.InnerHtml);//2
        xmlDocument.LoadXml(parserXML.ToHtml());

person Ogoniok    schedule 28.03.2018    source источник


Ответы (1)


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

В основе такого преобразования должна быть определена следующая функция. Мы заботимся только о 4 типах узлов — других не должно быть. Для простоты мы отбрасываем любые элементы или атрибуты пространства имен.

static XmlNode CreateNodeFrom(XmlDocument document, INode source)
{
    switch (source.NodeType)
    {
        case NodeType.Comment:
            return document.CreateComment(source.TextContent);
        case NodeType.Element:
            var original = (IElement)source;
            var element = document.CreateElement(original.LocalName);

            foreach (var attr in original.Attributes)
            {
                element.SetAttribute(attr.Name, attr.Value);
            }

            return element;
        case NodeType.Text:
            return document.CreateTextNode(source.TextContent);
        default:
            return null;
    }
}

Функция будет использоваться почти исключительно из конвертера дерева:

static void AppendChildren(INode source, XmlNode target)
{
    var owner = target.OwnerDocument;

    foreach (var child in source.ChildNodes)
    {
        var node = CreateNodeFrom(owner, child);
        target.AppendChild(node);
        AppendChildren(child, node);
    }
}

Итак, мы просто ходим по дереву и преобразуем то, что видим... Изменение исходной функции:

var config = Configuration.Default.WithCss().WithDefaultLoader();
var address = Url.Create("https://www.google.com/");
var document = BrowsingContext.New(config).OpenAsync(address).Result;
var xmlDocument = new XmlDocument();
var root = CreateNodeFrom(xmlDocument, document.DocumentElement);
xmlDocument.AppendChild(root);
AppendChildren(document.DocumentElement, root);

(Я не знаю, почему вы использовали все эти разные синтаксические анализаторы; синтаксические анализаторы работают на текстовом уровне, но HTML и XML несовместимы на текстовом уровне, поэтому давайте попробуем преобразовать на уровне DOM, где мы можем контролировать происходящее) .

Я удалил некоторую избыточность и улучшил ваш асинхронный->синхронный мост (но я бы посоветовал вам удалить .Result и использовать await, т. е. возвращать Task<XmlDocument> вместо XmlDocument).

Надеюсь это поможет!

person Florian Rappl    schedule 30.03.2018