如何使用正则表达式通过类名删除整个HTML标签(及其内容)?

7

我对正则表达式不是很熟练,但正在学习。

我想通过类名删除一些HTML标记。目前为止,我的代码如下:

<div class="footer".*?>(.*?)</div>

第一个 .*? 是因为它可能包含其他属性,第二个是因为它可能包含其他HTML内容。

我做错了什么?我尝试了很多设置但都没有成功。

更新

DIV内部可以包含多行文本,并且我正在使用Perl正则表达式进行操作。


你使用的目标语言是什么? - toolkit
我添加了Perl,但我可能需要在C#中使用它。 - Patrick Desjardins
8个回答

17

正如其他人所说,使用正则表达式处理HTML非常棘手,DOM方法可能更好。例如:

use HTML::TreeBuilder::XPath;

my $tree = HTML::TreeBuilder::XPath->new;
$tree->parse_file( 'yourdocument.html' );

for my $node ( $tree->findnodes( '//*[@class="footer"]' ) ) {
    $node->replace_with_content;   # delete element, but not the children
}

print $tree->as_HTML;

1
为了删除元素及其子元素,请将'replace_with_content'替换为'detach'。 - Yanick

15

在 div 标签中,您还需要允许其他事项在类之前。

<div[^>]*class="footer"[^>]*>(.*?)</div>

此外,要不区分大小写地进行操作。您可能需要转义引号或关闭标签中的斜杠等字符。您正在执行什么上下文操作?另外请注意,使用正则表达式解析HTML可能会非常复杂,这取决于输入情况。下面的答案提出了一个很好的观点-假设您有以下结构:

<div>
    <div class="footer">
        <div>Hi!</div>
    </div>
</div>

试图为此构建正则表达式是一种灾难性的做法。你最好将文档加载到DOM中,并对其进行操作。

伪代码应该与XML::DOM密切相关:

document = //load document
divs = document.getElementsByTagName("div");
for(div in divs) {
    if(div.getAttributes["class"] == "footer") {
        parent = div.getParent();
        for(child in div.getChildren()) {
            // filter attribute types?
            parent.insertBefore(div, child);
        }
        parent.removeChild(div);
    }
}


这里有一个Perl库,HTML:: DOM, 还有另一个XML::DOM。.NET内置了处理DOM解析的库。


当所有HTML在同一行中时,它可以工作,但缩进后就无法工作。为什么在 "footer" 中要使用 [^>] ? - Patrick Desjardins
使正则表达式确定性。大多数引擎可以处理不确定性,但有时会产生意外的结果。从技术上讲,在 [^>] 和 [c] 之间仍然存在非确定性问题,但它不太重要。 - Daniel Spiewak
寻找除了闭括号之外的任何内容 - Chris Marasti-Georg

1
<div[^>]*class="footer"[^>]*>(.*?)</div>

对我来说有效,但需要在特殊字符前使用反斜杠

<div[^>]*class=\"footer\"[^>]*>(.*?)<\/div>

1
在Perl中,您需要使用/s修饰符,否则点号将无法匹配换行符。
话虽如此,使用适当的HTML或XML解析器来删除HTML文件中不需要的部分更为合适。

0

部分取决于您使用的正则表达式引擎 - 使用的语言等。但一个可能性是您需要转义引号和/或斜杠。您还可以将其设置为不区分大小写。

<div class=\"footer\".*?>(.*?)<\/div>

否则请说明您正在使用的语言/平台——.NET、Java、Perl等。

请注意,此处需要使用 /s 选项,因为其中某些字符可能是换行符。 - brian d foy

0

试试这个:

<([^\s]+).*?class="footer".*?>([.\n]*?)</([^\s]+)>

你最大的问题可能会是嵌套标签。例如:

<div class="footer"><b></b></div>

给定的正则表达式将匹配到所有内容,直到</b>,留下</div>悬挂在末尾。你需要假设你要查找的标签没有嵌套元素,或者你需要使用一些从HTML到DOM的解析器和XPath查询来删除整个子树。

你可以在正则表达式的末尾使用第一个捕获组的反向引用... - Chris Marasti-Georg
给定的正则表达式不会匹配中间的标记。div标记内的惰性量词将在div结尾处的>停止匹配。因此,粗体标记将被(.*?)匹配,这正是我想要的。 - Hamish Downer
嗯,它要么太懒惰了,要么太贪心了。另一个答案给出了一个贪心匹配一个太多的闭合标签的例子。正则表达式对于这种情况来说并不足够强大。 - Daniel Spiewak

0

这将会很棘手,因为正则表达式的贪婪性,(请注意我的例子可能是特定于perl的,但我知道贪婪性是REs的一个普遍问题。)第二个.*?将尽可能匹配到</div>之前的所有内容,所以如果你有以下内容:

<div class="SomethingElse"><div class="footer"> stuff </div></div>

表达式将匹配:

<div class="footer"> stuff </div></div>

这不太可能是你想要的。


-3

为什么不使用 <div class="footer".*?</div> 呢?我也不是正则表达式专家,但我认为你不需要指定最后一个括号来匹配开放的 div 标签。


也许他想捕获 div 的内容? - Chris Marasti-Georg
是的,他说他想删除标签,而不是内容。 - Hamish Downer
那个正则表达式将捕获整个网页中第一个<div class="footer">和最后一个</div>之间的所有内容(除非perl函数没有使用它的多行模式)。 - user1228

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