Как исправить неправильно сформированный HTML с помощью HTML Agility Pack?

У меня есть этот плохо сформированный HTML с перекрывающимися тегами:

<p>word1<b>word2</p>
<p>word3</b>word4</p>

Перекрытие тоже может быть вложенным.

Как преобразовать его в правильно сформированный HTML с помощью HTML Agility Pack (HAP)?

Я ищу этот вывод:

<p>word1<b>word2</b></p>
<p><b>word3</b>word4</p>

Я попробовал HtmlNode.ElementsFlags["b"] = HtmlElementFlag.Closed | HtmlElementFlag.CanOverlap, но он не работает должным образом.


person avo    schedule 26.03.2014    source источник


Ответы (1)


На самом деле он работает так, как ожидалось, но, возможно, работает не так, как вы ожидали. В любом случае, вот образец кода (консольное приложение), который демонстрирует, как можно добиться некоторого исправления HTML с помощью библиотеки.

В библиотеке есть ParseErrors коллекция, которую можно использовать для определения ошибок, обнаруженных во время синтаксического анализа разметки.

Здесь действительно есть два типа проблем:

1) незакрытые элементы. Это исправлено библиотекой по умолчанию, но в элементе P есть опция, которая предотвращает это в этом случае.

2) неоткрытые элементы. Этот более сложный, потому что это зависит от того, как вы хотите его исправить, где вы хотите открыть тег? В следующем примере я использовал ближайший предыдущий текстовый родственный узел, чтобы открыть элемент.

static void Main(string[] args)
{
    // clear the flags on P so unclosed elements in P will be auto closed.
    HtmlNode.ElementsFlags.Remove("p");

    // load the document
    HtmlDocument doc = new HtmlDocument();
    doc.Load("yourTestFile.htm");

    // build a list of nodes ordered by stream position
    NodePositions pos = new NodePositions(doc);

    // browse all tags detected as not opened
    foreach (HtmlParseError error in doc.ParseErrors.Where(e => e.Code == HtmlParseErrorCode.TagNotOpened))
    {
        // find the text node just before this error
        HtmlTextNode last = pos.Nodes.OfType<HtmlTextNode>().LastOrDefault(n => n.StreamPosition < error.StreamPosition);
        if (last != null)
        {
            // fix the text; reintroduce the broken tag
            last.Text = error.SourceText.Replace("/", "") + last.Text + error.SourceText;
        }
    }

    doc.Save(Console.Out);
}

public class NodePositions
{
    public NodePositions(HtmlDocument doc)
    {
        AddNode(doc.DocumentNode);
        Nodes.Sort(new NodePositionComparer());
    }

    private void AddNode(HtmlNode node)
    {
        Nodes.Add(node);
        foreach (HtmlNode child in node.ChildNodes)
        {
            AddNode(child);
        }
    }

    private class NodePositionComparer : IComparer<HtmlNode>
    {
        public int Compare(HtmlNode x, HtmlNode y)
        {
            return x.StreamPosition.CompareTo(y.StreamPosition);
        }
    }

    public List<HtmlNode> Nodes = new List<HtmlNode>();
}
person Simon Mourier    schedule 31.03.2014
comment
Спасибо, Саймон, мне нужно это переварить :) Приятно слышать напрямую от автора, твоя работа над HAP высоко ценится. - person avo; 01.04.2014
comment
Есть ли особый случай на ‹frame›? Я спрашиваю только потому, что у меня неожиданное поведение, когда фрейм не закрывается автоматически. - person twobob; 14.12.2014
comment
@twobob - да, может быть, вы можете проверить это поведение здесь (фрейм объявлен, его можно удалить с помощью коллекции ElementFlags): htmlagilitypack.codeplex.com/SourceControl/latest#Trunk/ - person Simon Mourier; 14.12.2014
comment
Приветствую вас за создание такой полезной библиотеки. - person Saksham; 23.12.2014