在C#中解析HTML的部分内容

3

我需要从一个HTML字符串中解析出特定的部分。例如:

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<p>[section=quote]</p>
<p>Mauris at turpis nec dolor bibendum sollicitudin ac quis neque.</p>
<p>[/section]</p>

解析引用部分应返回:
<p>Mauris at turpis nec dolor bibendum sollicitudin ac quis neque.</p>

目前我正在使用正则表达式来获取 [section=quote]...[/section] 内的内容,但由于这些部分是使用所见即所得编辑器输入的,因此部分标记本身被包装在段落标记中,因此解析的结果是:

</p>
<p>Mauris at turpis nec dolor bibendum sollicitudin ac quis neque.</p>
<p>

我现在使用的正则表达式是:
\[section=(.+?)\](.+?)\[/section\]

在解析部分之前,我还要进行一些额外的清理工作:

protected string CleanHtml(string input) {
    // remove whitespace
    input = Regex.Replace(input, @"\s*(<[^>]+>)\s*", "$1", RegexOptions.Singleline);
    // remove empty p elements
    input = Regex.Replace(input, @"<p\s*/>|<p>\s*</p>", string.Empty);
    return input;
}

有没有人能提供一个正则表达式来实现我要找的东西,或者说我试图用正则表达式做这个事情是浪费时间吗?我看到了 Html Agility Pack 的参考资料 - 对于这样的事情,这个工具会更好吗?

[更新]

感谢 Oscar,我已经使用了 HTML Agility Pack 和正则表达式的组合来解析这些部分。它仍需要一些细化,但已经接近完成。

public void ParseSections(string content)
{
    this.SourceContent = content;
    this.NonSectionedContent = content;

    content = CleanHtml(content);

    if (!sectionRegex.IsMatch(content))
        return;

    var doc = new HtmlDocument();
    doc.LoadHtml(content);

    bool flag = false;
    string sectionName = string.Empty;
    var sectionContent = new StringBuilder();
    var unsectioned = new StringBuilder();

    foreach (var n in doc.DocumentNode.SelectNodes("//p")) {               
        if (startSectionRegex.IsMatch(n.InnerText)) { 
            flag = true;
            sectionName = startSectionRegex.Match(n.InnerText).Groups[1].Value.ToLowerInvariant();
            continue;
        }
        if (endSectionRegex.IsMatch(n.InnerText)) {
            flag = false;
            this.Sections.Add(sectionName, sectionContent.ToString());
            sectionContent.Clear();
            continue;
        }

        if (flag)
            sectionContent.Append(n.OuterHtml);
        else
            unsectioned.Append(n.OuterHtml);
    }

    this.NonSectionedContent = unsectioned.ToString();
}

4
强制性链接到https://dev59.com/X3I-5IYBdhLWcg3wq6do#1732454。 - stuartd
使用正则表达式解析HTML通常是个坏主意,因为HTML不是规则的。如果你能看一下HTML解析器,有很多可用的,它们会带来更少的痛苦。 - TheLukeMcCarthy
2个回答

2
以下代码使用 HtmlAgilityPack 库实现:
using HtmlAgilityPack;

...

HtmlDocument doc = new HtmlDocument();
doc.Load(@"C:\file.html");


bool flag = false;
var sb = new StringBuilder();
foreach (var n in doc.DocumentNode.SelectNodes("//p"))
{
    switch (n.InnerText)
    {
        case "[section=quote]":
            flag = true;
            continue;
        case "[/section]":
            flag = false;
            break;
    }
    if (flag)
    {
        sb.AppendLine(n.OuterHtml);
    }
}

Console.Write(sb);
Console.ReadLine();

如果您只想打印Mauris at turpis nec dolor bibendum sollicitudin ac quis neque.而不包括<p>...</p>,您可以用n.InnerHtml替换n.OuterHtml
当然,您应该检查doc.DocumentNode.SelectNodes("//p")是否为null
如果您想从在线源而不是文件中加载html,则可以执行以下操作:
var htmlWeb = new HtmlWeb();  
var doc = htmlWeb.Load("http://..../page.html");
编辑:

如果[section=quote]一个[/section]可以嵌套在任何标签中(不一定是<p>),你可以将doc.DocumentNode.SelectNodes("//p")替换为doc.DocumentNode.SelectNodes("//*")


哇,谢谢。我刚刚才注意到你的回复。让我试一下! - Ben Foster

1
如何替换


<p>[section=quote]</p>

使用

[section=quote]

并且

<p>[/section]</p>

使用

[/section]

作为您的清理的一部分。然后,您可以使用现有的正则表达式。


由于 HTML 内容完全由用户控制,我实际上不知道 [section] 标签将被包裹在什么中(可能是 div、p 或其他任何标签)。 - Ben Foster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接