Comment puis-je préserver les sauts de ligne lorsque j'utilise jsoup pour convertir html en texte brut?

j'ai le code suivant:

 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)));
}

et j'ai le résultat:

hello world yo googlez

mais je veux briser la ligne:

hello world
yo googlez

j'ai regardé le TextNode de jsoup#getWholeText () mais je n'arrive pas à trouver comment l'utiliser.

S'il y a un <br> dans le markup que j'analyse, comment puis-je obtenir une rupture de ligne dans ma sortie résultante?

83
demandé sur RAnders00 2011-04-12 23:11:02

15 réponses

la vraie solution qui préserve les linebreaks doit être comme ceci:

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));
}

il satisfait aux prescriptions suivantes:

  1. si le html original contient newline (\n), il est préservé
  2. si le html original contient des balises br ou p, elles sont traduites vers newline(\n).
78
répondu user121196 2016-03-11 23:26:28

avec

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

vous avez de la sortie

"A B" 

et non

A

B

pour ceci j'utilise:

descrizione = Jsoup.parse(html.replaceAll("(?i)<br[^>]*>", "br2n")).text();
text = descrizione.replaceAll("br2n", "\n");
42
répondu Mirco Attocchi 2016-12-10 07:31:49
Jsoup.clean(unsafeString, "", Whitelist.none(), new OutputSettings().prettyPrint(false));

nous utilisons cette méthode ici:

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

en le passant Whitelist.none() nous nous assurons que tout HTML est supprimé.

par passing new OutputSettings().prettyPrint(false) nous nous assurons que la production n'est pas reformatée et les sauts de ligne sont préservés.

41
répondu Paulius Z 2014-08-19 11:26:20

essayez ceci en utilisant 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));
}
17
répondu mkowa 2013-06-24 15:42:37

vous pouvez traverser un élément donné

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();               
}

et pour votre code

String result = convertNodeToText(JSoup.parse(html))
5
répondu popcorny 2013-08-01 11:31:48
text = Jsoup.parse(html.replaceAll("(?i)<br[^>]*>", "br2n")).text();
text = descrizione.replaceAll("br2n", "\n");

fonctionne si le html lui-même ne contient pas" br2n "

,

text = Jsoup.parse(html.replaceAll("(?i)<br[^>]*>", "<pre>\n</pre>")).text();

fonctionne plus fiable et plus facile.

3
répondu Green Beret 2014-07-24 04:53:08

C'est ma version de traduire html en texte (la version modifiée de user121196 answer, en fait).

cela ne préserve pas seulement les sauts de ligne, mais aussi le formatage du texte et la suppression des sauts de ligne excessifs, les symboles HTML escape, et vous obtiendrez un bien meilleur résultat de votre HTML (dans mon cas je le reçois par courrier).

il est écrit à L'origine en Scala, mais vous pouvez le changer en Java facilement""

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()      
}
3
répondu abdolence 2016-06-05 13:23:46

essayez ceci:

public String noTags(String str){
    Document d = Jsoup.parse(str);
    TextNode tn = new TextNode(d.body().html(), "");
    return tn.getWholeText();
}
2
répondu manji 2011-04-12 20:08:59

utilisez textNodes() pour obtenir une liste des noeuds de texte. Puis les concaténer avec \n comme séparateur. Voici du code scala que j'utilise pour cela, le port java devrait être facile:

val rawTxt = doc.body().getElementsByTag("div").first.textNodes()
                    .asScala.mkString("<br />\n")
2
répondu Michael Bar-Sinai 2013-09-18 17:02:12

pour HTML plus complexe aucune des solutions ci-dessus n'a fonctionné tout à fait correctement; j'ai pu réussir la conversion tout en préservant les sauts de ligne avec:

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

(version 1.10.3)

2
répondu Andy Res 2017-09-21 12:49:05
/**
 * 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;
}

utilisé en appelant avec le html en question, contenant le br, avec n'importe quelle chaîne de caractères que vous souhaitez utiliser comme le placeholder newline temporaire. Par exemple:

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

la récursion veillera à ce que la chaîne que vous utilisez comme placeholder newline/linebreaker ne sera jamais réellement dans le html source, car elle continuera à ajouter un" 1 " jusqu'à ce que la chaîne de placeholder linkbreaker ne se trouve pas dans le html. Il n'y aura pas de problème de formatage Jsoup.les méthodes propres semblent rencontrer des caractères spéciaux.

1
répondu Chris6647 2014-01-27 21:16:34

basé sur la réponse de l'utilisateur 121196 et du béret vert avec les select s et <pre> s, la seule solution qui fonctionne pour moi est:

org.jsoup.nodes.Element elementWithHtml = ....
elementWithHtml.select("br").append("<pre>\n</pre>");
elementWithHtml.select("p").prepend("<pre>\n\n</pre>");
elementWithHtml.text();
1
répondu Bevor 2016-05-31 18:14:18

D'après les autres réponses et les commentaires sur cette question, il semble que la plupart des gens qui viennent ici sont vraiment à la recherche d'une solution générale qui fournira une représentation en texte simple bien formatée d'un document HTML. Je sais que j'ai été.

heureusement JSoup déjà fournir un exemple assez complet de la façon d'atteindre ce: HtmlToPlainText.java

L'exemple FormattingVisitor peut facilement être modifié à votre préférence et traite de la plupart des éléments de bloc et de l'enroulement de ligne.

pour éviter la pourriture de lien, voici Jonathan Hedley 's solution in full:

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.
 * <p>
 * Note that this is a fairly simplistic formatter -- for real world use you'll want to embrace and extend.
 * </p>
 * <p>
 * To invoke from the command line, assuming you've downloaded the jsoup jar to your current directory:</p>
 * <p><code>java -cp jsoup.jar org.jsoup.examples.HtmlToPlainText url [selector]</code></p>
 * where <i>url</i> is the URL to fetch, and <i>selector</i> 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();
        }
    }
}
1
répondu Malcolm Smith 2017-05-23 12:18:21

essayez ceci en utilisant jsoup:

    doc.outputSettings(new OutputSettings().prettyPrint(false));

    //select all <br> tags and append \n after that
    doc.select("br").after("\n");

    //select all <p> 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");
1
répondu Abhay Gupta 2017-09-08 19:38:32

sur Jsoup v1.11.2, nous pouvons maintenant utiliser Element.wholeText() .

exemple de code:

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

user121196's réponse fonctionne toujours. Mais wholeText() préserve l'alignement des textes.

0
répondu zeenosaur 2018-05-17 14:04:40