从一个字符串中移除HTML标签

486

有没有一种好的方法从Java字符串中删除HTML?像简单的正则表达式

replaceAll("\\<.*?>", "") 

会起作用,但是像&amp;这样的东西不会正确地转换,两个尖括号之间的非HTML内容将被删除(即正则表达式中的.*?将消失)。


4
请使用以下指南,并编译 'org.jsoup:jsoup:1.9.2'。 - VahidHoseini
2
https://dev59.com/UXVC5IYBdhLWcg3wnCaA#3149645 - VahidHoseini
参见:https://dev59.com/UXVC5IYBdhLWcg3wnCaA#21838532 - Stephan
35个回答

653

20
Jsoup很好用,但我也遇到了一些问题。我使用它来防止跨站脚本攻击,所以基本上我期望的是纯文本输入,但有些不怀好意的人可能会尝试发送一些HTML。使用Jsoup,我可以删除所有HTML标记,但不幸的是它也将许多空格缩减为一个并删除换行符(\n字符)。 - Ridcully
8
您希望使用Jsoup#clean()来实现这一点。它是一个白名单过滤器,可以让您从HTML中删除所有不被允许的标记和属性,而不会更改原始内容。有关详细信息,请参见http://jsoup.org/cookbook/cleaning-html/whitelist-sanitizer。 - BalusC
5
使用 clean() 方法仍会导致额外的空格和换行符被删除。 例如,Jsoup.clean("a \n b", Whitelist.none()) 返回 "a b"。 - Keith
24
@Zeroows说:“在<p>Lorem ipsum 1 < 3 dolor sit amet</p>上这种方法彻底失败了。再次强调,HTML不是正则语言。我完全不明白为什么每个人都试图在它上面使用正则表达式来解析感兴趣的内容,而不是使用真正的解析器。” - BalusC
8
使用Jsoup.clean(unsafeString, "", Whitelist.none(), new OutputSettings().prettyPrint(false));保留换行符。 - Marc Johnen
显示剩余17条评论

302

如果你正在为Android编写代码,可以这样做...

androidx.core.text.HtmlCompat.fromHtml(instruction, HtmlCompat.FROM_HTML_MODE_LEGACY).toString()

注:此处为代码示例,不提供解释。

12
棒极了的提示。 :) 如果你在 TextView 中显示文本,你可以省略 .toString() 来保留一些格式。 - Lorne Laliberte
1
@Branky 它不行,我已经尝试过了... 接受的答案运行得非常好。 - Maverick
非常好用。所有HTML标签都已从字符串中删除。 - user3144836
3
这很好,但是<img>标签被替换成了一些奇怪的东西。在原本应该有图片的地方出现了小方块。 - Bibaswann Bandyopadhyay
1
@BibaswannBandyopadhyay 另一个答案帮助 摆脱这些字符。 - Vince
1
请使用androidx.core.text包代替旧版的android.text - Ricard

98

如果用户输入 <b>hey!</b>,你想要显示 <b>hey!</b> 还是 hey!? 如果是前者,只需要将小于号(<)进行转义并对安帕赛德符号进行 HTML 编码(可选引号),即可。如果是后者,则需要对代码进行修改:

replaceAll("\\<[^>]*>","")

如果用户输入了类似于<bhey!</b>这样格式不正确的内容,你将会遇到问题。

你还可以查看JTidy,它将解析“脏”的HTML输入,并应该为您提供一种删除标记、保留文本的方法。

试图剥离HTML的问题在于浏览器有着非常宽松的解析器,比你能找到的任何库都要宽松,因此即使你尽你最大努力剥离所有标记(使用上面的替换方法、DOM库或JTidy),你仍然需要确保编码任何剩余的HTML特殊字符以保持输出安全。


2
如果HTML节点内容中存在未经转义的<或>符号,您也会遇到问题。例如:<span>我的年龄是<a lot's of text>然后是你的年龄</span>。我认为,唯一完全解决此问题的方法是通过某些XML DOM接口(如SAX或类似工具),使用node.getText()函数进行操作。 - Mitja Gustin
这段代码适用于像 "\r\n HDFC Bank </a>\r\n </div>\r\n </td>\r\n" 这样的字符串。 - vikramvi

31
另一种方法是使用javax.swing.text.html.HTMLEditorKit来提取文本。
import java.io.*;
import javax.swing.text.html.*;
import javax.swing.text.html.parser.*;

public class Html2Text extends HTMLEditorKit.ParserCallback {
    StringBuffer s;

    public Html2Text() {
    }

    public void parse(Reader in) throws IOException {
        s = new StringBuffer();
        ParserDelegator delegator = new ParserDelegator();
        // the third parameter is TRUE to ignore charset directive
        delegator.parse(in, this, Boolean.TRUE);
    }

    public void handleText(char[] text, int pos) {
        s.append(text);
    }

    public String getText() {
        return s.toString();
    }

    public static void main(String[] args) {
        try {
            // the HTML to convert
            FileReader in = new FileReader("java-new.html");
            Html2Text parser = new Html2Text();
            parser.parse(in);
            in.close();
            System.out.println(parser.getText());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

6
"a < b or b > c" 的结果是 "a < b 或者 b > c",这看起来很不幸。 - dfrankow
2
这对我来说效果最好。我需要保留换行符。我通过向解析器添加这个简单的方法来实现:@Override public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) { if (t == HTML.Tag.P || t == HTML.Tag.BR) { s.append('\n'); } } - MiguelMunoz
3
数学表达式 a < b 或 b > c 在 HTML 中应该这样写:a < b 或 b > c。 - MiguelMunoz
1
我喜欢这个没有外部依赖。 - Matthieu

26

我认为过滤HTML标签最简单的方法是:

private static final Pattern REMOVE_TAGS = Pattern.compile("<.+?>");

public static String removeTags(String string) {
    if (string == null || string.length() == 0) {
        return string;
    }

    Matcher m = REMOVE_TAGS.matcher(string);
    return m.replaceAll("");
}

20

在 Android 上,尝试这样做:

String result = Html.fromHtml(html).toString();

1
你总是在普通代码中使用代码片段。代码片段只应用于可以在浏览器中运行的HTML、JavaScript或其他代码。你不能在浏览器中运行Java。以后请使用普通代码块...我会为你编辑答案并修复格式等问题,但请不要再这样做了。这不是我第一次告诉你这个问题了... - Xaver Kapeller
1
@PaulCroarkin 这是 Android SDK 内部的库。android.text.Html - Ameen Maheen
2
太棒了。已删除所有HTML标签。 - user3144836
2
看起来很熟悉,就像我在2011年的答案。 - Ken Goodridge
1
这让我从另一个烦恼中解脱了出来 :) - nathandrake
显示剩余5条评论

19

使用Jericho非常简单,您可以保留某些格式(例如换行和链接)。

    Source htmlSource = new Source(htmlText);
    Segment htmlSeg = new Segment(htmlSource, 0, htmlSource.length());
    Renderer htmlRend = new Renderer(htmlSeg);
    System.out.println(htmlRend.toString());

4
Jericho能够将<br>解析为换行符,而Jsoup和HTMLEditorKit无法做到这一点。 - homaxto
Jericho非常有能力完成这项工作,在自己的项目中经常使用它。 - Jerry Tian
3
杰里科的运作非常顺利。感谢您的建议。请注意:您不必创建整个字符串的段落。源扩展段落,因此在渲染器构造函数中两者都可以使用。 - MrPlow
Jerico现在似乎有点过时了(最后一次发布是2015年底的3.4版本)。然而,如果它仍然运行良好,那么它仍然可以使用! - Jonathan Hult

18

使用简单的Jsoup.parse(html).text()作为答案有两个潜在问题(JSoup 1.7.3):

  • 它会从文本中删除换行符
  • 它将文本&lt;script&gt;转换为<script>

如果你使用它来防止XSS攻击,这有点麻烦。以下是我最好的改进方案,同时使用JSoup和Apache StringEscapeUtils:

// breaks multi-level of escaping, preventing &amp;lt;script&amp;gt; to be rendered as <script>
String replace = input.replace("&amp;", "");
// decode any encoded html, preventing &lt;script&gt; to be rendered as <script>
String html = StringEscapeUtils.unescapeHtml(replace);
// remove all html tags, but maintain line breaks
String clean = Jsoup.clean(html, "", Whitelist.none(), new Document.OutputSettings().prettyPrint(false));
// decode html again to convert character entities back into text
return StringEscapeUtils.unescapeHtml(clean);

请注意,最后一步是因为我需要将输出作为纯文本使用。如果您只需要HTML输出,则应该能够将其删除。

这里是一堆测试用例(输入输出):

{"regular string", "regular string"},
{"<a href=\"link\">A link</a>", "A link"},
{"<script src=\"http://evil.url.com\"/>", ""},
{"&lt;script&gt;", ""},
{"&amp;lt;script&amp;gt;", "lt;scriptgt;"}, // best effort
{"\" ' > < \n \\ é å à ü and & preserved", "\" ' > < \n \\ é å à ü and & preserved"}

如果你找到了使它变得更好的方法,请告诉我。


2
这将无法对抗像&#38;lt;script&#38;gt;alert('Evil script executed');&#38;lt;/script&#38;gt;这样的内容。同样适用于&#x26;。JSoup不会将&lt;script&gt;转换为<script>,它之所以这样做是因为在JSoup清理输入后调用了StringEscapeUtils.unescapeHtml - Guillaume Polet

12

这应该有效 -

使用这个

  text.replaceAll('<.*?>' , " ") -> This will replace all the html tags with a space.

而且这个

  text.replaceAll('&.*?;' , "")-> this will replace all the tags which starts with "&" and ends with ";" like &nbsp;, &amp;, &gt; etc.

1
通常,如果答案包括对代码意图的解释,则更有助于解决问题。 - Peter
@Sandeep1699 没有任何解释答案?不好。 - Arefe

12

HTML转义真的很难做到正确-我强烈建议使用库代码来处理,因为这比你想象的要微妙得多。查看Apache的StringEscapeUtils,这是一个相当不错的Java库。


1
这正是我要找的东西,但我想剥离HTML而不是转义它。 - Mason
你想去除HTML标签,还是将其转换为纯文本?从带有br标签和HTML实体的长字符串中去除HTML可能会导致难以阅读的混乱。 - Tim Howland
1
你尝试过使用commons-lang中的StringEscapeUtils.unescapeHtml吗? - Rafael Sanches
5
StringEscapeUtils.unescapeHtml方法不会去除HTML标签。 - Erin Drummond
7
有关用于反转义的实用工具的良好信息,但没有回答问题。 - Alex
4
混淆的回答。移除!= 反转义 - Lluis Martinez

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