在TextContent中保留(或恢复)空格

时间:2015-12-29 14:57:05

标签: c# anglesharp

使用AngleSharp处理一些HTML并提取元素的文本内容以供以后挖掘,我遇到了AngleSharp剥离HTML标记的问题。例如,我有一段像这样的HTML(减去换行符和制表符):

<div id="someID">
    blah, blah, blah, blah
    blah, blah, 
    <ul>
        <li><i>action.</i></li>
        <li><i>Typical, blah, blah, blah</li>
    </ul>
    blah, blah, blah
</div>

这里的问题是,当我得到TextContent

var content = someDiv.TextContext;

它会像这样出现:

"...blah, blah, action.Typical blah, blah..."

单词actionTypical在没有任何空格的情况下被粉碎(因为它们之间唯一的东西是html标签)。这会使我的努力绊倒,然后将文本内容标记化,因为action.Typical被视为单个单词而不是两个单词。

当然,我可以运行搜索并替换(可能使用正则表达式),例如(\S)\.(\S),并将其替换为$1. $2,但这样会使用www.somecompany.com之类的内容并将其拆分为wwwsomecompanycom,而可能希望保留该值(或www和{{1}失败无论如何都不可能非常有用)。我可以排除包含多个点的字词,但网址可能显示为com(不包含somecompany.com),或者您可能会遇到www这样的电子邮件地址。

这是否有一种强有力的方法?在标签被剥离后至少保留一个空格?

2 个答案:

答案 0 :(得分:0)

因此,解决此问题的最佳方法似乎是向下递归根元素的ChildNodes(不是Children错过文本节点),然后再次将它们连接起来。所以,给定:

var rootElem = myDoc.GetElementById("someId");

我可以创建一个这样的函数:

IEnumerable<string> ExtractChildNodes(INode node)
{
    if (node.HasChildNodes)
    {
        foreach (var c in node.ChildNodes)
        {
            foreach (var r in ExtractChildNodes(c))
            {
                yield return r;
            }
        }
    }
    else
    {
        yield return node.TextContent;
    }
}

这将测试节点是否具有子节点,以及是否向下钻取到最低叶节点并从那里返回文本。我可以这样做:

var textContentWithSpacesBetweenNodes = string.Join(" ", ExtractChildNodes(rootElem));

应该给我:

"...blah, blah, action. Typical blah, blah..."

actionTypical之间的空格。

这似乎不仅可以处理像<p>some.</p><p>words</p>这样的情况,还可以解决像some</br>words甚至some<br>words这样的自我关闭标签,这对处理使用正则表达式或类似问题会非常痛苦类似。

答案 1 :(得分:0)

您描述的方式有效,除了您已遇到的某些方案(例如,自动关闭标记)。因此,我提出以下建议:

  • 文字节点将按字面意思
  • 元素遍历其子节点,w.r.t。
    • 如果两个相邻元素产生内容,则插入空格
    • 如果没有子节点,则查看该元素是否特殊(例如br)并放置一些代表性字符串(例如换行符)
    • 否则,例如,如果文本节点与元素相邻,则不插入文本

总的来说,以下实现应该完成这项工作:

String Stringify(INode node)
{
    switch (node.NodeType)
    {
        case NodeType.Text:
            return node.TextContent;

        case NodeType.Element:
            if (node.HasChildNodes)
            {
                var sb = new StringBuilder();
                var isElement = false;

                foreach (var child in node.ChildNodes)
                {
                    var isPreviousElement = isElement;
                    var content = Stringify(child);
                    isElement = child.NodeType == NodeType.Element;

                    if (!String.IsNullOrEmpty(content) && isElement && isPreviousElement)
                    {
                        sb.Append(' ');
                    }

                    sb.Append(content);
                }

                return sb.ToString();
            }

            switch (node.NodeName.ToLowerInvariant())
            {
                case "br": return "\n";
            }

            goto default;

        default:
            return String.Empty;

    }
}

这种实施的优势在于您可以根据自己的需要进行调整。例如,对于br等标记,您可以轻松输出空格而不是换行符。