Html'yi düz metne dönüştürmek için jsoup kullanırken satır sonlarını nasıl koruyabilirim?


101

Takip koduna sahibim:

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

Ve sonuca sahibim:

hello world yo googlez

Ama çizgiyi kırmak istiyorum:

hello world
yo googlez

Jsoup'un TextNode # getWholeText () öğesine baktım ama nasıl kullanılacağını çözemiyorum .

<br>Ayrıştırdığım işaretlemede bir varsa , elde ettiğim çıktıda nasıl satır sonu alabilirim?


metninizi düzenleyin - sorunuzda satır sonu görünmüyor. Genel olarak, her şeyin doğru görünüp görünmediğini kontrol etmek için lütfen göndermeden önce sorunuzun önizlemesini okuyun.
Robin Green

Aynı soruyu sordum (jsoup gereksinimi olmadan) ama hala iyi bir çözümüm yok: stackoverflow.com/questions/2513707/…
Eduardo

@zeenosaur'un cevabına bakın.
Jang-Ho Bae

Yanıtlar:


102

Satır sonlarını koruyan gerçek çözüm şu şekilde olmalıdır:

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

Aşağıdaki gereksinimleri karşılar:

  1. orijinal html satırsonu (\ n) içeriyorsa, korunur
  2. orijinal html br veya p etiketleri içeriyorsa, bunlar yeni satıra (\ n) çevrilir.

5
Bu seçilen cevap olmalıdır
Duy

2
br2nl, en yararlı veya doğru yöntem adı değildir
DD.

2
Bu en iyi cevap. Peki for (Element e : document.select("br")) e.after(new TextNode("\n", ""));sıra yerine gerçek satırsonu eklemeye ne dersiniz \ n? Fark için bkz. Node :: after () ve Elements :: append () . replaceAll()Bu durumda gerekli değildir. P ve diğer blok elemanlarına benzer.
user2043553

1
@ user121196'nın cevabı seçilen cevap olmalıdır. Girdi HTML'sini temizledikten sonra hala HTML varlıklarınız varsa, Jsoup clean'den çıktıya StringEscapeUtils.unescapeHtml (...) Apache commons uygulayın.
karth500


44
Jsoup.clean(unsafeString, "", Whitelist.none(), new OutputSettings().prettyPrint(false));

Bu yöntemi burada kullanıyoruz:

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

Geçerek Whitelist.none()tüm HTML'nin kaldırıldığından emin oluruz.

Geçiş new OutputSettings().prettyPrint(false)yaparak çıktının yeniden biçimlendirilmediğinden ve satır sonlarının korunduğundan emin oluruz.


Bu tek doğru cevap olmalıdır. Diğerleri, yalnızca bretiketlerin yeni satırlar ürettiğini varsayar . Ne HTML herhangi diğer blok elemanı hakkında gibi div, p, ulvs? Hepsi de yeni çizgiler sunuyor.
adarshr

7
Bu çözümle html "<html> <body> <div> 1. satır </div> <div> 2. satır </div> <div> 3. satır </div> </body> </html>" çıktı: yeni satırlar olmadan "satır 1 satır 2 satır 3".
JohnC

2
Bu benim için çalışmıyor; <br> satır sonları oluşturmuyor.
JoshuaD

43

İle

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

çıktınız var

"A B" 

ve yok

A

B

Bunun için kullanıyorum:

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

2
Aslında bu kolay bir palyatiftir, ancak IMHO bu tamamen Jsoup kitaplığı tarafından ele alınmalıdır (şu anda bunun gibi birkaç rahatsız edici davranışa sahiptir - aksi takdirde harika bir kitaplıktır!).
SRG

5
JSoup size DOM vermiyor mu? Neden tüm <br>öğeleri yeni satırlar içeren metin düğümleriyle değiştirip sonra .text()bir normal ifade dönüşümü yapmak yerine çağırmıyorsunuz ki bu, bazı dizeler için yanlış çıktıya neden olacak<div title=<br>'not an attribute'></div>
Mike Samuel

5
Güzel, ama bu "descrizione" nereden geliyor?
Steve Waters

"descrizione" düz metnin atandığı değişkeni temsil eder
enigma969

23

Bunu jsoup kullanarak deneyin:

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

güzel beni küçük bir değişiklikle işe yarıyor new Document.OutputSettings().prettyPrint(true)
Ashu

Bu çözüm "& nbsp;" onları bir boşluğa ayrıştırmak yerine metin olarak.
Andrei Volgin

13

Jsoup v1.11.2'de artık kullanabiliriz Element.wholeText().

Örnek kod:

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

user121196's cevap hala çalışıyor. Ancak wholeText()metinlerin hizalamasını korur.


Süper güzel özellik!
Denis Kulagin

8

Daha karmaşık HTML için yukarıdaki çözümlerin hiçbiri tam olarak doğru çalışmadı; Aşağıdakilerle satır sonlarını korurken dönüşümü başarıyla gerçekleştirdim:

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

(sürüm 1.10.3)


1
Tüm cevapların en iyisi! Andy Res teşekkürler!
Bharath Nadukatla

6

Belirli bir öğeyi geçebilirsiniz

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

Ve kodunuz için

String result = convertNodeToText(JSoup.parse(html))

Ben eğer test etmeli miyim isBlockiçinde tail(node, depth)yerine ve ekleme \nonu girerken yerine bloğu ayrılırken? Bunu yapıyorum (yani kullanıyorum tail) ve bu iyi çalışıyor. Ancak headsizin gibi kullanırsam , bu: <p>line one<p>line twotek bir satır olarak biter.
KajMagnus

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

html'nin kendisi "br2n" içermiyorsa çalışır

Yani,

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

daha güvenilir ve daha kolay çalışır.


4

Bunu jsoup kullanarak deneyin:

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

3

textNodes()Metin düğümlerinin bir listesini almak için kullanın . Sonra bunları \nayırıcı olarak birleştirin . İşte bunun için kullandığım bazı scala kodu, java portu kolay olmalı:

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

3

Diğer cevaplara ve bu soruyla ilgili yorumlara dayanarak, buraya gelen çoğu insanın gerçekten bir HTML belgesinin güzel bir şekilde biçimlendirilmiş düz metin temsilini sağlayacak genel bir çözüm aradığı görülmektedir. Ben olduğumu biliyorum.

Neyse ki JSoup, bunun nasıl başarılacağına dair oldukça kapsamlı bir örnek sunuyor: HtmlToPlainText.java

Örnek FormattingVisitor , tercihinize göre kolayca değiştirilebilir ve çoğu blok öğesi ve satır kaydırmayla ilgilenir.

Bağlantı çürümesini önlemek için, Jonathan Hedley'in tam çözümü:

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

3

Bu, html'yi metne çevirme versiyonumdur (aslında user121196 cevabının değiştirilmiş versiyonu).

Bu sadece satır sonlarını korumakla kalmaz, aynı zamanda metni biçimlendirir ve aşırı satır sonlarını, HTML kaçış sembollerini kaldırır ve HTML'nizden çok daha iyi bir sonuç alırsınız (benim durumumda postadan alıyorum).

Orijinal olarak Scala'da yazılmıştır, ancak kolayca Java'ya dönüştürebilirsiniz

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> etiketlerinin başına da yeni bir satır eklemeniz gerekir. Aksi takdirde, bir div <a> veya <span> etiketlerini takip ederse, yeni bir satırda olmayacaktır.
Andrei Volgin

2

Bunu dene:

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

1
<p> <b> merhaba dünya </b> </p> <p> <br /> <b> hey </b> <a href=" google.com"> googlez </a> </ p > ama merhaba dünyaya ihtiyacım var yo googlez (html etiketleri olmadan)
Billy

Bu cevap düz metin olarak dönmez; satırsonu eklenmiş HTML'yi döndürür.
KajMagnus

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

Geçici satırsonu yer tutucusu olarak kullanmak istediğiniz dizeyle birlikte br içeren, söz konusu html ile çağrılarak kullanılır. Örneğin:

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

Özyineleme, bağlantı kırıcı yer tutucu dizesi html'de bulunmayana kadar "1" eklemeye devam edeceğinden, satırsonu / satır ayırıcı yer tutucusu olarak kullandığınız dizenin aslında hiçbir zaman kaynak html'de olmamasını sağlar. Jsoup.clean yöntemlerinin özel karakterlerle karşılaştığı biçimlendirme sorunu olmayacak.


İyiydi, ama özyinelemeye ihtiyacınız yok, sadece şu satırı ekleyin: while (dirtyHTML.contains (linebreakerString)) linebreakerString = linebreakerString + "1";
Dr NotSoKind

Ah evet. Tamamen doğru. Sanırım zihnim bir kez olsun özyinelemeyi kullanabildiğim için takıldı :)
Chris6647

1

User121196 en ve ile yeşil bereli Yanıta göre selects ve <pre>s, benim için çalışır tek çözüm:

org.jsoup.nodes.Element elementWithHtml = ....
elementWithHtml.select("br").append("<pre>\n</pre>");
elementWithHtml.select("p").prepend("<pre>\n\n</pre>");
elementWithHtml.text();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.