.NET 移除/剥离 HTML 页面中的 JavaScript 和 CSS 代码块。

8

我有一个带有JavaScript和CSS代码块的HTML字符串:

<script type="text/javascript">

  alert('hello world');

</script>

<style type="text/css">
  A:link {text-decoration: none}
  A:visited {text-decoration: none}
  A:active {text-decoration: none}
  A:hover {text-decoration: underline; color: red;}
</style>

如何去除这些块?有什么建议可以用来删除它们的正则表达式吗?
5个回答

20

一种快速且不精确的方法是使用如下正则表达式:

var regex = new Regex(
   "(\\<script(.+?)\\</script\\>)|(\\<style(.+?)\\</style\\>)", 
   RegexOptions.Singleline | RegexOptions.IgnoreCase
);

string ouput = regex.Replace(input, "");

更好(但可能较慢)的选择是使用HtmlAgilityPack

HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(htmlInput);

var nodes = doc.DocumentNode.SelectNodes("//script|//style");

foreach (var node in nodes)
    node.ParentNode.RemoveChild(node);

string htmlOutput = doc.DocumentNode.OuterHtml;

*) 有关为什么这样做更好的讨论,请参见此主题


3
你知道Tony The Pony吗? - GvS
1
@GvS:我知道在使用正则表达式处理HTML时可能会出现的问题。因此,对于大多数情况,我强烈建议使用像HtmlAgilityPack这样的HTML解析器,但这取决于具体情况。如果只是一次性批处理以删除脚本和样式块,并且我知道输入是有效的HTML,则上面的正则表达式足以满足要求,特别是因为<script>标签和<style>标签不能有嵌套标签。 - Elian Ebbing
3
@GvS:我添加了一个使用HtmlAgilityPack的示例。 - Elian Ebbing
1
要小心内联脚本,例如<body onload="doSomething()">。需要更加复杂的工具来消除它。 - Vinnie Amir

2

使用HTMLAgilityPack可以获得更好的结果

或者尝试这个函数

public string RemoveScriptAndStyle(string HTML)
{
    string Pat = "<(script|style)\\b[^>]*?>.*?</\\1>";
    return Regex.Replace(HTML, Pat, "", RegexOptions.IgnoreCase | RegexOptions.Singleline);
}

1

只需查找一个开放的<script标签,然后删除它和关闭的/script>标签之间的所有内容。

同样适用于样式。请参阅Google获取字符串操作技巧。


如果你的代码中有document.write("</script>"),它就无法正常工作。 - kͩeͣmͮpͥ ͩ
从安全角度来看,仅仅防止 JavaScript 执行就足够了吗? - Bamboo

1

我制作了一个自行车) 它可能不像HtmlAgilityPack那样正确,但在400 kb的页面上要快5-6倍。还要将符号变成小写并删除数字(为分词器而制作)

 private static readonly List<byte[]> SPECIAL_TAGS = new List<byte[]>
                                                            {
                                                                Encoding.ASCII.GetBytes("script"),
                                                                Encoding.ASCII.GetBytes("style"),
                                                                Encoding.ASCII.GetBytes("noscript")
                                                            };

    private static readonly List<byte[]> SPECIAL_TAGS_CLOSE = new List<byte[]>
                                                                  {
                                                                      Encoding.ASCII.GetBytes("/script"),
                                                                      Encoding.ASCII.GetBytes("/style"),
                                                                      Encoding.ASCII.GetBytes("/noscript")};

public static string StripTagsCharArray(string source, bool toLowerCase)
    {
        var array = new char[source.Length];
        var arrayIndex = 0;
        var inside = false;
        var haveSpecialTags = false;
        var compareIndex = -1;
        var singleQouteMode = false;
        var doubleQouteMode = false;
        var matchMemory = SetDefaultMemory(SPECIAL_TAGS);
        for (int i = 0; i < source.Length; i++)
        {
            var let = source[i];
            if (inside && !singleQouteMode && !doubleQouteMode)
            {
                compareIndex++;
                if (haveSpecialTags)
                {
                    var endTag = CheckSpecialTags(let, compareIndex, SPECIAL_TAGS_CLOSE, ref matchMemory);
                    if (endTag) haveSpecialTags = false;
                }
                if (!haveSpecialTags)
                {
                    haveSpecialTags = CheckSpecialTags(let, compareIndex, SPECIAL_TAGS, ref matchMemory);
                }
            }
            if (haveSpecialTags && let == '"')
            {
                doubleQouteMode = !doubleQouteMode;
            }
            if (haveSpecialTags && let == '\'')
            {
                singleQouteMode = !singleQouteMode;
            }
            if (let == '<')
            {
                matchMemory = SetDefaultMemory(SPECIAL_TAGS);
                compareIndex = -1;
                inside = true;
                continue;
            }
            if (let == '>')
            {
                inside = false;
                continue;
            }
            if (inside) continue;
            if (char.IsDigit(let)) continue; 
            if (haveSpecialTags) continue;
            array[arrayIndex] = toLowerCase ? Char.ToLowerInvariant(let) : let;
            arrayIndex++;
        }
        return new string(array, 0, arrayIndex);
    }

    private static bool[] SetDefaultMemory(List<byte[]> specialTags)
    {
        var memory = new bool[specialTags.Count];
        for (int i = 0; i < memory.Length; i++)
        {
            memory[i] = true;
        }
        return memory;
    }

1
与Elian Ebbing和Rajeev的答案类似,我选择使用HTML库而不是正则表达式来获得更稳定的解决方案。但是,我使用了AngleSharp而不是HtmlAgilityPack,在.NET Core 3中提供了类似于jquery的选择器:
//using AngleSharp;
var context = BrowsingContext.New(Configuration.Default);
var document = await context.OpenAsync(req => req.Content(sourceHtml)); // generate HTML DOM from source html string
var elems = document.QuerySelectorAll("script, style"); // get script and style elements
foreach(var elem in elems)
{
    var parent = elem.Parent;
    parent.RemoveChild(elem); // remove element from DOM
}
var resultHtml = document.DocumentElement.OuterHtml; // HTML result as a string

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