3

使用 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并将其拆分为wwwsomecompany并且com可能想保留它(或失败www并且com它们本身不太可能非常有用)。我可以排除带有多个点的单词,但网址可能会显示为somecompany.com(不带www),或者您可能会遇到类似的电子邮件地址somebody@somecompany.com

有没有一种强大的方法来解决这个问题?在标签被剥离后至少保留一个空格?

4

2 回答 2

0

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

  • 文本节点将按字面意思表示
  • 元素迭代它们的子节点,wrt
    • 如果两个相邻元素产生内容,则插入一个空格
    • 如果没有子节点,则查看元素是否特殊(例如 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您可以轻松输出空格而不是换行符的标签。

于 2016-01-04T15:20:04.997 回答
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..."

action和之间有空格Typical

这似乎不仅可以处理诸如<p>some.</p><p>words</p>自动关闭标签之类的情况some</br>words,甚至some<br>words可以处理使用正则表达式或类似内容的巨大痛苦。

于 2015-12-29T16:16:37.703 回答