使用jsoup将html转换为纯文本时如何保留行间距?

114

我有以下代码:

 public class NewClass {
     public String noTags(String str){
         return Jsoup.parse(str).text();
     }


     public static void main(String args[]) {
         String strings="<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN \">" +
         "<HTML> <HEAD> <TITLE></TITLE> <style>body{ font-size: 12px;font-family: verdana, arial, helvetica, sans-serif;}</style> </HEAD> <BODY><p><b>hello world</b></p><p><br><b>yo</b> <a href=\"http://google.com\">googlez</a></p></BODY> </HTML> ";

         NewClass text = new NewClass();
         System.out.println((text.noTags(strings)));
}

这是我的结果:

hello world yo googlez

但我想要换行:

hello world
yo googlez

我查看了jsoup的TextNode#getWholeText(),但我无法弄清如何使用它。

如果在我解析的标记中有<br>,如何在我的输出中获得换行?


请编辑您的文本——您的问题中没有显示出换行符。通常在发布问题之前,请预览您的问题,以检查是否一切正常显示。 - Robin Green
我问了同样的问题(没有jsoup要求),但我仍然没有一个好的解决方案:https://dev59.com/-EzSa4cB1Zd3GeqPpcj8 - Eduardo
see @zeenosaur 's answer. - Jang-Ho Bae
15个回答

3
这是我翻译HTML转文本的版本(实际上是用户121196答案的修改版)。
这不仅保留了换行符,还格式化文本并删除过多的换行符和HTML转义符号,从HTML中获得更好的结果(在我的情况下,我从邮件中接收它)。
它最初是用Scala编写的,但你可以轻松地将其改为Java。
def html2text( rawHtml : String ) : String = {

    val htmlDoc = Jsoup.parseBodyFragment( rawHtml, "/" )
    htmlDoc.select("br").append("\\nl")
    htmlDoc.select("div").prepend("\\nl").append("\\nl")
    htmlDoc.select("p").prepend("\\nl\\nl").append("\\nl\\nl")

    org.jsoup.parser.Parser.unescapeEntities(
        Jsoup.clean(
          htmlDoc.html(),
          "",
          Whitelist.none(),
          new org.jsoup.nodes.Document.OutputSettings().prettyPrint(true)
        ),false
    ).
    replaceAll("\\\\nl", "\n").
    replaceAll("\r","").
    replaceAll("\n\\s+\n","\n").
    replaceAll("\n\n+","\n\n").     
    trim()      
}

你需要在<div>标签前面添加一个新行。否则,如果<div>标签跟在<a>或<span>标签后面,它将不会换行。 - Andrei Volgin

3

试试这个:

public String noTags(String str){
    Document d = Jsoup.parse(str);
    TextNode tn = new TextNode(d.body().html(), "");
    return tn.getWholeText();
}

1
你好世界 嘿,googlez - Billy
这个答案不会返回纯文本,而是插入了新行的HTML。 - KajMagnus

3
使用textNodes()函数获取文本节点列表,然后使用\n作为分隔符进行连接。以下是我用于此的Scala代码,Java版本应该很容易实现:
val rawTxt = doc.body().getElementsByTag("div").first.textNodes()
                    .asScala.mkString("<br />\n")

1

根据用户121196和Green Beret的答案,结合select<pre>标签,对我来说唯一可行的解决方案是:

org.jsoup.nodes.Element elementWithHtml = ....
elementWithHtml.select("br").append("<pre>\n</pre>");
elementWithHtml.select("p").prepend("<pre>\n\n</pre>");
elementWithHtml.text();

1
/**
 * Recursive method to replace html br with java \n. The recursive method ensures that the linebreaker can never end up pre-existing in the text being replaced.
 * @param html
 * @param linebreakerString
 * @return the html as String with proper java newlines instead of br
 */
public static String replaceBrWithNewLine(String html, String linebreakerString){
    String result = "";
    if(html.contains(linebreakerString)){
        result = replaceBrWithNewLine(html, linebreakerString+"1");
    } else {
        result = Jsoup.parse(html.replaceAll("(?i)<br[^>]*>", linebreakerString)).text(); // replace and html line breaks with java linebreak.
        result = result.replaceAll(linebreakerString, "\n");
    }
    return result;
}

使用时需调用相关的HTML,并包含换行符,同时需要使用您想要用作临时换行符号的任何字符串。例如:

replaceBrWithNewLine(element.html(), "br2n")

递归将确保您用作换行符/分隔符占位符的字符串永远不会实际出现在源html中,因为它将不断添加“1”,直到在html中找不到链接断点占位符字符串。它不会像Jsoup.clean方法遇到特殊字符时那样存在格式问题。

不错,但是你不需要递归,只需添加这一行代码: while(dirtyHTML.contains(linebreakerString)) linebreakerString = linebreakerString + "1"; - Dr NotSoKind
啊,是的。完全正确。我想我的思维被卡在了能够使用递归的情况下 :) - Chris6647

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