提取HTML正文内容的正则表达式

22

我正在寻找一个正则表达式语句,可以让我从XHTML文档的body标签之间提取HTML内容。

我需要解析的XHTML文件非常简单,不用担心JavaScript内容或<![CDATA[标记,例如。

下面是我要解析的HTML文件的预期结构。由于我确切地知道我将要处理的HTML文件的所有内容,因此这个HTML片段基本上涵盖了我全部的用例。如果我能够得到一个正则表达式来提取这个示例的主体,我就会很满意。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>
    </title>
  </head>
  <body contenteditable="true">
    <p>
      Example paragraph content
    </p>
    <p>
      &nbsp;
    </p>
    <p>
      <br />
      &nbsp;
    </p>
    <h1>Header 1</h1>
  </body>
</html>

从概念上讲,我一直在尝试构建一个正则表达式字符串,该字符串匹配除内部主体内容以外的所有内容。使用这个正则表达式,我会使用C#中的Regex.Split()方法来获取主体内容。我认为这个正则表达式是:

((.|\n)*<body (.)*>)|((</body>(*|\n)*)

...看起来这应该可以解决问题,但是在RegexBuddy中测试我的内容时似乎根本不起作用。


Split() 不是这个任务的正确工具。只需使用 Regex.Match(subject, "(?s)<body[^>]*>(.*)</body>").Groups[1].Value - Alan Moore
6个回答

24

这个能行吗?

((?:.(?!<body[^>]*>))+.<body[^>]*>)|(</body\>.+)

当然,您需要添加必要的 \s 以考虑到带有空格的 <body...> 元素,例如:

当然,你需要在正则表达式中添加必要的\s以便考虑到带有空格的<body...>元素,就像这样:

((?:.(?!<\s*body[^>]*>))+.<\s*body[^>]*>)|(<\s*/\s*body\s*\>.+)

再想一想,我不确定为什么需要负向先行断言...这样也可以工作(对于一个格式良好的xhtml文档):

(.*<\s*body[^>]*>)|(<\s*/\s*body\s*\>.+)

嗯,看起来这是一个很好的案例,可以证明正则表达式不应该用于(未知的)HTML: <body onload="DoSomething('>');"> 是有效的... :-) - PhiLho
PhiLho,你错了,这不是有效的XHTML。 ">"必须转义为">"才能成为XML格式。然而,Web浏览器使用各种技巧来读取损坏的HTML / XHTML。带有JavaScript内容的页面通常不是XML格式,除非它们被放置在CDATA中。 - Hendy Irawan
谢谢您。您能否稍微描述一下如何在C#中使用Regex库来返回HTML文档字符串中所需的内容? - A H
1
十二年后,我相信这个答案更多地关于要使用的正则表达式,而不是它的C#用法。对于后者,我会遵循https://www.c-sharpcorner.com/article/c-sharp-regex-examples/中的一个示例。 - VonC

9

XHTML使用XML解析器比正则表达式更容易解析。我知道这不是你要问的,但XML解析器能够快速定位到body节点并返回其内容,而无需像正则表达式一样遇到标记映射问题。

编辑: 回应评论中提到的问题;即XML解析器太慢。

有两种类型的XML解析器,一种称为DOM,它很大、重量级、易于使用和友好,它在您执行任何操作之前就将文档构建成树形结构。另一种称为SAX,它快速、轻巧、更耗费精力,它按顺序读取文件。您需要使用SAX来查找Body标记。

DOM方法适用于多种用途,可以提取标记并查找谁是谁的子元素。SAX解析器按顺序读取文件,并且可以快速获取所需信息。正则表达式不会比SAX解析器更快,因为它们都只是在文件中进行模式匹配,唯一的例外是正则表达式在找到body标记后不会停止查找,因为正则表达式没有内置的XML知识。实际上,您的SAX解析器可能使用小型的正则表达式片段来查找每个标记。


4
无需重新发明轮子。如果它是XHTML,那么它就是XML,而XML解析器是该工作的工具。+1 - Adam Jaskiewicz
这是我尝试的第一个解决方案,但它似乎运行得非常缓慢。我想正则表达式会更快。 - Matthew Ruston
1
有两种XML解析器,一种称为DOM,它很大、笨重、易用和友好,它在您执行任何操作之前会将文档构建成树形结构。另一种称为SAX,它快速、轻便、工作量更大,它按顺序读取文件。您将需要使用SAX来查找Body标签。 - Karl
1
这对于解析器来说是一个非常简单的任务,它真的不应该很慢。 - annakata
我最初尝试使用.NET的System.Xml.XmlDocument类,如果这能解释任何速度缓慢的问题。- Matthew Ruston - Matthew Ruston
即使速度较慢,它也将处理所有异常情况,如name="</body>"等。 - bezmax

5
String toMatch="aaaaaaaaaaabcxx sldjfkvnlkfd <body>i m avinash</body>";
Pattern pattern=Pattern.compile(".*?<body.*?>(.*?)</body>.*?");
Matcher matcher=pattern.matcher(toMatch);
if(matcher.matches()) {
    System.out.println(matcher.group(1));
}

3
/<body[^>]*>(.*)</body>/s

替换为

\1

这样可以将整个文档匹配并将主体放入\3中。因此,如果未完全匹配当前文档的格式,您就知道该文档的格式还需要考虑其他内容,并且可以引发错误。 - Kev
我知道这是一个非常旧的帖子,但是我喜欢这个答案,所以必须让它被认可。 - Stephane Gosselin
1
请注意,在Perl正则表达式和Java中,您必须打开s标志以强制内部的(.*)匹配换行符,这通常是您想要的。在我看来,没有必要有开始或结束分组-一个更简单的答案是/<body[^>]*>(.*)</body>/s并使用第1组。 - aarestad
1
@aarestad 谢谢,我已经编辑了,你说得对——那时我的正则表达式技能还不够熟练 :) - Kev

3
为什么不能用

来分割呢?
</{0,1}body[^>]*> 

并且取第二个字符串?我相信这比查找一个巨大的正则表达式要快得多。


话虽如此,如果你解决了那个问题,你的方法可能会更简单。 :) - Kev
好的,我在你发表评论并编辑了这个答案之前就已经注意到了它 :P - bezmax
我实际上没有足够的积分来编辑...可能是别人。 - Kev

1

匹配第一个body标签:<\s*body.*?>

匹配最后一个body标签:<\s*/\s*body.*?>

(注意:我们考虑标签中间的空格,这是完全有效的标记)

将它们组合在一起,就像这样,您将获得包括body标签在内的所有内容:<\s*body.*?>.*?<\s*/\s*body.*?>。并确保您使用Singleline模式,它将忽略换行符。

这适用于VB.NET,希望也适用于其他语言!


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