Разбор 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());

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).

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

Другие вопросы по тегам