¿Cómo puedo preservar los saltos de línea cuando uso jsoup para convertir html a texto sin formato?

Tengo el siguiente código:

public class NewClass { public String noTags(String str){ return Jsoup.parse(str).text(); } public static void main(String args[]) { String strings="" + "   body{ font-size: 12px;font-family: verdana, arial, helvetica, sans-serif;}  

hello world


yo googlez

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

Y tengo el resultado:

 hello world yo googlez 

Pero quiero romper la línea:

 hello world yo googlez 

He visto el TextNode # de jsoup getWholeText () pero no sé cómo usarlo.

Si hay un
en el marcado que analizo, ¿cómo puedo obtener un salto de línea en mi resultado resultante?

La solución real que preserva los saltos de línea debería ser así:

 public static String br2nl(String html) { if(html==null) return html; Document document = Jsoup.parse(html); document.outputSettings(new Document.OutputSettings().prettyPrint(false));//makes html() preserve linebreaks and spacing document.select("br").append("\\n"); document.select("p").prepend("\\n\\n"); String s = document.html().replaceAll("\\\\n", "\n"); return Jsoup.clean(s, "", Whitelist.none(), new Document.OutputSettings().prettyPrint(false)); } 

Satisface los siguientes requisitos:

  1. si el html original contiene nueva línea (\ n), se conserva
  2. si el html original contiene tags br o p, se traduce a nueva línea (\ n).
 Jsoup.clean(unsafeString, "", Whitelist.none(), new OutputSettings().prettyPrint(false)); 

Estamos usando este método aquí:

 public static String clean(String bodyHtml, String baseUri, Whitelist whitelist, Document.OutputSettings outputSettings) 

Whitelist.none() nos aseguramos de que se elimine todo el HTML.

Al pasar new OutputSettings().prettyPrint(false) nos aseguramos de que la salida no se formatee y se new OutputSettings().prettyPrint(false) líneas.

Con

 Jsoup.parse("A\nB").text(); 

usted tiene salida

 "AB" 

y no

 A B 

Para esto estoy usando:

 descrizione = Jsoup.parse(html.replaceAll("(?i)]*>", "br2n")).text(); text = descrizione.replaceAll("br2n", "\n"); 

Pruebe esto usando jsoup:

 public static String cleanPreserveLineBreaks(String bodyHtml) { // get pretty printed html with preserved br and p tags String prettyPrintedBodyFragment = Jsoup.clean(bodyHtml, "", Whitelist.none().addTags("br", "p"), new OutputSettings().prettyPrint(true)); // get plain text with preserved line breaks by disabled prettyPrint return Jsoup.clean(prettyPrintedBodyFragment, "", Whitelist.none(), new OutputSettings().prettyPrint(false)); } 

Puedes atravesar un elemento dado

 public String convertNodeToText(Element element) { final StringBuilder buffer = new StringBuilder(); new NodeTraversor(new NodeVisitor() { boolean isNewline = true; @Override public void head(Node node, int depth) { if (node instanceof TextNode) { TextNode textNode = (TextNode) node; String text = textNode.text().replace('\u00A0', ' ').trim(); if(!text.isEmpty()) { buffer.append(text); isNewline = false; } } else if (node instanceof Element) { Element element = (Element) node; if (!isNewline) { if((element.isBlock() || element.tagName().equals("br"))) { buffer.append("\n"); isNewline = true; } } } } @Override public void tail(Node node, int depth) { } }).traverse(element); return buffer.toString(); } 

Y para tu código

 String result = convertNodeToText(JSoup.parse(html)) 
 text = Jsoup.parse(html.replaceAll("(?i)]*>", "br2n")).text(); text = descrizione.replaceAll("br2n", "\n"); 

funciona si el html en sí no contiene “br2n”

Asi que,

 text = Jsoup.parse(html.replaceAll("(?i)]*>", "
\n

")).text();

funciona más confiable y más fácil.

Esta es mi versión de traducir html a texto (la versión modificada de la respuesta user121196, en realidad).

Esto no solo preserva los saltos de línea, sino también el formato del texto y la eliminación de saltos de línea excesivos, símbolos de escape HTML, y obtendrás un resultado mucho mejor de tu HTML (en mi caso, lo recibo del correo).

Está escrito originalmente en Scala, pero puedes cambiarlo a Java fácilmente

 def html2text( rawHtml : String ) : String = { val htmlDoc = Jsoup.parseBodyFragment( rawHtml, "/" ) htmlDoc.select("br").append("\\nl") htmlDoc.select("div").append("\\nl") htmlDoc.select("p").prepend("\\nl\\nl") htmlDoc.select("p").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() } 

Prueba esto:

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

Use textNodes() para obtener una lista de los nodos de texto. Luego concatenarlos con \n como separador. Aquí hay un código de scala que uso para esto, el puerto de Java debería ser fácil:

 val rawTxt = doc.body().getElementsByTag("div").first.textNodes() .asScala.mkString("
\n")

Para HTML más complejo, ninguna de las soluciones anteriores funcionó correctamente; Pude realizar la conversión de forma exitosa y conservar saltos de línea con:

 Document document = Jsoup.parse(myHtml); String text = new HtmlToPlainText().getPlainText(document); 

(versión 1.10.3)

 /** * 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)]*>", linebreakerString)).text(); // replace and html line breaks with java linebreak. result = result.replaceAll(linebreakerString, "\n"); } return result; } 

Se utiliza al llamar con el html en cuestión, que contiene el br, junto con cualquier cadena que desee usar como marcador de posición temporal de la nueva línea. Por ejemplo:

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

La recursión asegurará que la cadena que use como marcador de posición newline / linebreaker nunca estará realmente en el html fuente, ya que seguirá agregando un “1” hasta que la cadena del marcador de posición linkbreaker no se encuentre en el html. No tendrá el problema de formato que los métodos Jsoup.clean parecen encontrar con caracteres especiales.

Basándome en las respuestas de user121196 y Green Beret con las select sy


s, la única solución que funciona para mí es:

 org.jsoup.nodes.Element elementWithHtml = .... elementWithHtml.select("br").append("
\n

"); elementWithHtml.select("p").prepend("

\n\n

"); elementWithHtml.text();

En base a las otras respuestas y los comentarios sobre esta pregunta, parece que la mayoría de las personas que vienen aquí realmente buscan una solución general que proporcione una representación de texto sin formato con un formato agradable de un documento HTML. Sé que lo era.

Afortunadamente JSoup ya proporciona un ejemplo bastante completo de cómo lograr esto: HtmlToPlainText.java

El ejemplo FormattingVisitor se puede modificar fácilmente según sus preferencias y se ocupa de la mayoría de los elementos de bloque y del ajuste de línea.

Para evitar la pudrición del enlace, aquí está la solución de Jonathan Hedley en su totalidad:

 package org.jsoup.examples; import org.jsoup.Jsoup; import org.jsoup.helper.StringUtil; import org.jsoup.helper.Validate; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.nodes.Node; import org.jsoup.nodes.TextNode; import org.jsoup.select.Elements; import org.jsoup.select.NodeTraversor; import org.jsoup.select.NodeVisitor; import java.io.IOException; /** * HTML to plain-text. This example program demonstrates the use of jsoup to convert HTML input to lightly-formatted * plain-text. That is divergent from the general goal of jsoup's .text() methods, which is to get clean data from a * scrape. * 

* Note that this is a fairly simplistic formatter -- for real world use you'll want to embrace and extend. *

*

* To invoke from the command line, assuming you've downloaded the jsoup jar to your current directory:

*

java -cp jsoup.jar org.jsoup.examples.HtmlToPlainText url [selector]

* where url is the URL to fetch, and selector is an optional CSS selector. * * @author Jonathan Hedley, jonathan@hedley.net */ public class HtmlToPlainText { private static final String userAgent = "Mozilla/5.0 (jsoup)"; private static final int timeout = 5 * 1000; public static void main(String... args) throws IOException { Validate.isTrue(args.length == 1 || args.length == 2, "usage: java -cp jsoup.jar org.jsoup.examples.HtmlToPlainText url [selector]"); final String url = args[0]; final String selector = args.length == 2 ? args[1] : null; // fetch the specified URL and parse to a HTML DOM Document doc = Jsoup.connect(url).userAgent(userAgent).timeout(timeout).get(); HtmlToPlainText formatter = new HtmlToPlainText(); if (selector != null) { Elements elements = doc.select(selector); // get each element that matches the CSS selector for (Element element : elements) { String plainText = formatter.getPlainText(element); // format that element to plain text System.out.println(plainText); } } else { // format the whole doc String plainText = formatter.getPlainText(doc); System.out.println(plainText); } } /** * Format an Element to plain-text * @param element the root element to format * @return formatted text */ public String getPlainText(Element element) { FormattingVisitor formatter = new FormattingVisitor(); NodeTraversor traversor = new NodeTraversor(formatter); traversor.traverse(element); // walk the DOM, and call .head() and .tail() for each node return formatter.toString(); } // the formatting rules, implemented in a breadth-first DOM traverse private class FormattingVisitor implements NodeVisitor { private static final int maxWidth = 80; private int width = 0; private StringBuilder accum = new StringBuilder(); // holds the accumulated text // hit when the node is first seen public void head(Node node, int depth) { String name = node.nodeName(); if (node instanceof TextNode) append(((TextNode) node).text()); // TextNodes carry all user-readable text in the DOM. else if (name.equals("li")) append("\n * "); else if (name.equals("dt")) append(" "); else if (StringUtil.in(name, "p", "h1", "h2", "h3", "h4", "h5", "tr")) append("\n"); } // hit when all of the node's children (if any) have been visited public void tail(Node node, int depth) { String name = node.nodeName(); if (StringUtil.in(name, "br", "dd", "dt", "p", "h1", "h2", "h3", "h4", "h5")) append("\n"); else if (name.equals("a")) append(String.format(" <%s>", node.absUrl("href"))); } // appends text to the string builder with a simple word wrap method private void append(String text) { if (text.startsWith("\n")) width = 0; // reset counter if starts with a newline. only from formats above, not in natural text if (text.equals(" ") && (accum.length() == 0 || StringUtil.in(accum.substring(accum.length() - 1), " ", "\n"))) return; // don't accumulate long runs of empty spaces if (text.length() + width > maxWidth) { // won't fit, needs to wrap String words[] = text.split("\\s+"); for (int i = 0; i < words.length; i++) { String word = words[i]; boolean last = i == words.length - 1; if (!last) // insert a space if not the last word word = word + " "; if (word.length() + width > maxWidth) { // wrap and reset counter accum.append("\n").append(word); width = word.length(); } else { accum.append(word); width += word.length(); } } } else { // fits as is, without need to wrap text accum.append(text); width += text.length(); } } @Override public String toString() { return accum.toString(); } } }

Pruebe esto usando jsoup:

  doc.outputSettings(new OutputSettings().prettyPrint(false)); //select all 
tags and append \n after that doc.select("br").after("\\n"); //select all

tags and prepend \n before that doc.select("p").before("\\n"); //get the HTML from the document, and retaining original new lines String str = doc.html().replaceAll("\\\\n", "\n");

En Jsoup v1.11.2, ahora podemos usar Element.wholeText() .

Código de ejemplo:

 String cleanString = Jsoup.parse(htmlString).wholeText(); 

user121196's respuesta user121196's todavía funciona. Pero wholeText() preserva la alineación de los textos.