এইচটিএমএলকে সরল পাঠ্যে রূপান্তর করতে jsoup ব্যবহার করার সময় আমি কীভাবে লাইন ব্রেকগুলি সংরক্ষণ করব?


103

আমার কাছে নিম্নলিখিত কোড রয়েছে:

 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>আমি পার্স করা মার্কআপে যদি একটি থাকে তবে আমি আমার ফলাফলের আউটপুটটিতে কীভাবে একটি লাইন বিরতি পেতে পারি?


আপনার পাঠ্য সম্পাদনা করুন - আপনার প্রশ্নে কোনও লাইন ব্রেক দেখা যাচ্ছে না। সাধারণভাবে পোস্ট করার আগে আপনার প্রশ্নের পূর্বরূপটি পড়ুন, সবকিছু ঠিকঠাক দেখাচ্ছে কিনা তা পরীক্ষা করতে।
রবিন গ্রিন

আমিও একই প্রশ্ন (jsoup প্রয়োজন ছাড়া) জিজ্ঞাসা কিন্তু আমি এখনও একটি ভাল সমাধান হবে না: stackoverflow.com/questions/2513707/...
এদুয়ার্দো

@ জেনোসৌরের উত্তর দেখুন।
জাং-হো বায়ে

উত্তর:


102

লাইনব্রেকগুলি সংরক্ষণ করে এমন আসল সমাধানটি এর মতো হওয়া উচিত:

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

এটি নিম্নলিখিত প্রয়োজনীয়তাগুলি পূরণ করে:

  1. যদি মূল এইচটিএমএলতে নতুন লাইন থাকে (\ n), এটি সংরক্ষণ করা হবে
  2. যদি মূল এইচটিএমএলতে বিআর বা পি ট্যাগ থাকে তবে সেগুলিকে নতুন লাইনে অনুবাদ করা হবে () n)

5
এটি নির্বাচিত উত্তর হওয়া উচিত
ডুয়ে

4
br2nl সবচেয়ে সহায়ক বা সঠিক পদ্ধতির নাম নয়
ডিডি।

4
এটি সেরা উত্তর। তবে কীভাবে for (Element e : document.select("br")) e.after(new TextNode("\n", ""));আসল নিউলাইন যুক্ত হবে এবং ক্রমটি নয়? দেখুন নোড :: পর () এবং উপাদানসমূহ :: পরিশেষে যোগ () পার্থক্য জন্য। replaceAll()এই ক্ষেত্রে প্রয়োজন না হয়। পি এবং অন্যান্য ব্লক উপাদানগুলির জন্য একই।
ব্যবহারকারী2043553

4
@ ব্যবহারকারী 121196 এর উত্তরটি নির্বাচিত উত্তর হওয়া উচিত। আপনি ইনপুট এইচটিএমএল পরিষ্কার করার পরেও যদি আপনার এইচটিএমএল সত্তা থাকে তবে স্ট্রিংইস্কিউটুলটি.উনেস্কেএইচটিএমএল প্রয়োগ করুন (...) অ্যাপাচি Jsoup ক্লিন থেকে আউটপুটে কম্পন করে।
karth500

6
এই সমস্যার বিস্তৃত উত্তরের জন্য github.com/jhy/jsoup/blob/master/src/main/java/org/jsoup/… দেখুন ।
ম্যালকম স্মিথ

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

আমরা এই পদ্ধতিটি এখানে ব্যবহার করছি:

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

এটি পাস করার মাধ্যমে Whitelist.none()আমরা নিশ্চিত করে নিই যে সমস্ত এইচটিএমএল সরানো হয়েছে।

পাস্কিংয়ের মাধ্যমে new OutputSettings().prettyPrint(false)আমরা নিশ্চিত হয়েছি যে আউটপুটটি পুনরায় ফর্ম্যাট করা হয়নি এবং লাইন ব্রেকগুলি সংরক্ষণ করা হয়েছে।


এটি একমাত্র সঠিক উত্তর হওয়া উচিত। অন্যরা সবাই ধরে নেয় যে কেবল brট্যাগগুলিই নতুন লাইন তৈরি করে। যেমন HTML এ অন্য কোন ব্লক উপাদান সম্পর্কে কি div, p, ulইত্যাদি? তাদের সবাই নতুন লাইন প্রবর্তন।
আদর্শ

7
এই সমাধানের সাথে, এইচটিএমএল "<html> <body> <ডিভি> লাইন 1 </div> <ডিভ> লাইন 2 </div> <ডিভ> লাইন 3 </div> </body> </html>" উত্পাদিত আউটপুট: কোনও নতুন লাইন ছাড়াই "লাইন 1line 2line 3"।
জনসি

4
এটি আমার পক্ষে কাজ করে না; <br> লাইন ব্রেকগুলি তৈরি করছে না।
জোশুয়াডি

44

সঙ্গে

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

আপনার আউটপুট আছে

"A B" 

এবং না

A

B

এর জন্য আমি ব্যবহার করছি:

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

4
প্রকৃতপক্ষে এটি একটি সহজ উপশমকারী, তবে আইএমএইচও এটি পুরোপুরি জাসুপ লাইব্রেরি দ্বারা পরিচালিত হওয়া উচিত (যা এই মুহুর্তে এর মতো কয়েকটি বিঘ্নিত আচরণ রয়েছে - অন্যথায় এটি দুর্দান্ত গ্রন্থাগার!)।
এসআরজি

5
জাসপ আপনাকে ডিওএম দেয় না? কেন কেবল সমস্ত <br>উপাদানগুলিকে নতুন লাইনের সমন্বিত পাঠ্য নোডগুলি প্রতিস্থাপন করবেন না এবং তারপরে .text()একটি রেজেক্স ট্রান্সফর্ম করার পরিবর্তে কল করুন যা কিছু স্ট্রিংয়ের জন্য ভুল আউটপুট তৈরি করবে<div title=<br>'not an attribute'></div>
মাইক স্যামুয়েল

5
ভাল লাগছে, তবে সেই "দেশপ্রেসিয়োন" কোথা থেকে এসেছে?
স্টিভ ওয়াটারস

"বর্ণনা" পরিবর্তনশীল প্লেইন টেক্সট নির্ধারিত পরার প্রতিনিধিত্ব করে
enigma969

23

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

চমৎকার এটি আমাকে একটি ছোট পরিবর্তন নিয়ে কাজ করে new Document.OutputSettings().prettyPrint(true)
আশু

এই সমাধানটি "& nbsp;" এ চলে যায় স্থান হিসাবে পার্স করার পরিবর্তে পাঠ্য হিসাবে।
আন্দ্রে ভলগিন

15

Jsoup v1.11.2 এ, আমরা এখন ব্যবহার করতে পারি Element.wholeText()

উদাহরণ কোড:

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

user121196's উত্তর এখনও কাজ করে। তবে wholeText()পাঠ্যগুলির সারিবদ্ধতা সংরক্ষণ করে।


সুপার-সুন্দর বৈশিষ্ট্য!
ডেনিস কুলাগিন

8

আরও জটিল এইচটিএমএলের জন্য উপরের সমাধানগুলির মধ্যে কোনওটিই ঠিক মতো কাজ করেনি; এর সাথে লাইন ব্রেকগুলি সংরক্ষণ করার সময় আমি সাফল্যের সাথে রূপান্তর করতে সক্ষম হয়েছি:

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

(সংস্করণ 1.10.3)


4
সব উত্তরের সেরা! ধন্যবাদ অ্যান্ডি রেস!
ভরথ নাদুকাতলা

6

আপনি একটি প্রদত্ত উপাদান অতিক্রম করতে পারেন

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

এবং আপনার কোড জন্য

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

আমার মনে হয় আপনি যদি পরীক্ষা করা উচিত isBlockমধ্যে tail(node, depth)পরিবর্তে, এবং পরিশেষে যোগ \nথাকলে যখন প্রবেশ বদলে ব্লক ছাড়ার? আমি এটি করছি (অর্থাত্ ব্যবহার করে tail) এবং এটি দুর্দান্ত কাজ করে। তবে আমি যদি headআপনার মতো ব্যবহার করি তবে এটি: <p>line one<p>line twoএকক লাইন হিসাবে শেষ হয়।
কাজম্যাগনুস

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

এইচটিএমএল নিজেই "br2n" না থাকলে কাজ করে

সুতরাং,

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

আরও নির্ভরযোগ্য এবং সহজ কাজ করে।


4

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

3

এটা চেষ্টা কর:

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

4
<p> <b> হ্যালো ওয়ার্ল্ড </ b> </p> <p> <br /> <b> ইও </ b> <a href=" google.com"> গুগল </a> </ p > তবে আমার হ্যালো ওয়ার্ল্ড ইও গুগলজ দরকার (এইচটিএমএল ট্যাগ ছাড়া)
বিলি

এই উত্তরটি সহজ পাঠ্য দেয় না; এটি linesোকানো নতুন লাইনের সাথে এইচটিএমএল ফেরত দেয়।
কাজম্যাগনুস

3

textNodes()পাঠ্য নোডগুলির একটি তালিকা পেতে ব্যবহার করুন । তারপরে তাদেরকে \nবিভাজক হিসাবে যুক্ত করুন। আমি এর জন্য কিছু স্কাল কোড ব্যবহার করছি, জাভা বন্দরটি সহজ হওয়া উচিত:

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

3

অন্যান্য উত্তর এবং এই প্রশ্নের মন্তব্যের উপর ভিত্তি করে মনে হয় যে এখানে আসা বেশিরভাগ লোকেরা সত্যিই একটি সাধারণ সমাধান খুঁজছেন যা একটি HTML ডকুমেন্টের সুন্দর বিন্যাসিত সরল পাঠ্য উপস্থাপনা সরবরাহ করবে। আমি জানি আমি ছিলাম

ভাগ্যক্রমে JSoup এটি কীভাবে অর্জন করবেন তার ইতিমধ্যে একটি বিস্তৃত উদাহরণ প্রদান করেছেন: HtmlToPlainText.java

উদাহরণটি FormattingVisitorসহজেই আপনার পছন্দটিকে টুইট করতে পারে এবং বেশিরভাগ ব্লক উপাদানসমূহ এবং লাইন মোড়কের বিষয়ে কাজ করে।

লিঙ্ক পচা এড়াতে, এখানে জোনাথন হেডলির সম্পূর্ণ সমাধান রয়েছে:

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

এটি আমার পাঠ্য থেকে এইচটিএমএল অনুবাদ করার সংস্করণ (ব্যবহারকারী 121196 উত্তরের পরিবর্তিত সংস্করণ, আসলে)।

এটি কেবল লাইন ব্রেকগুলি সংরক্ষণ করে না, তবে পাঠ্যকে ফর্ম্যাট করে এবং অতিরিক্ত লাইন বিরতি, এইচটিএমএল এস্কেপ প্রতীকগুলি সরিয়ে দেয় এবং আপনি আপনার এইচটিএমএল থেকে আরও ভাল ফলাফল পাবেন (আমার ক্ষেত্রে এটি আমি মেল থেকে পাচ্ছি)।

এটি মূলত স্কালায় লেখা হয়েছে তবে আপনি এটি সহজেই জাভাতে পরিবর্তন করতে পারেন

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

আপনাকে <ডিভ> ট্যাগগুলিতেও একটি নতুন লাইন প্রিপেন্ড করতে হবে। অন্যথায়, যদি কোনও ডিভি << বা <স্প্যান> ট্যাগ অনুসরণ করে তবে এটি কোনও নতুন লাইনে থাকবে না।
আন্দ্রে ভলগিন

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

অস্থায়ী নিউলাইন স্থানধারক হিসাবে আপনি যে স্ট্রিংটি ব্যবহার করতে চান তার পাশাপাশি, প্রশ্নযুক্ত এইচটিএমএলকে কল করে ব্যবহার করা হয়। উদাহরণ স্বরূপ:

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

পুনরাবৃত্তিটি নিশ্চিত করবে যে আপনি যে স্ট্রিংটিকে নিউলাইন / লাইনব্রেকার স্থানধারক হিসাবে ব্যবহার করেন তা আসলে উত্স এইচটিএমএলে কখনই থাকবে না, কারণ এটি লিঙ্কব্রেকার স্থানধারক স্ট্রিংটি এইচটিএমএলে পাওয়া যায় না ততক্ষণ "1" যোগ করতে থাকবে। এটির ফর্ম্যাটিং ইস্যুটি নেই যা জসপ.প্লান পদ্ধতিগুলি বিশেষ অক্ষরের সাথে দেখা বলে মনে হচ্ছে।


ভাল, তবে আপনার পুনরাবৃত্তি দরকার নেই, কেবল এই লাইনটি যুক্ত করুন: যখন (নোংরা এইচটিএমএল.কন্টেনস (লাইনব্রেকার স্ট্রিং) লাইনব্রেকার স্ট্রিং = লাইনব্রেকার স্ট্রিং + "1";
ডাঃ নটসোকাইন্ড

অই হ্যাঁ. সম্পূর্ণ সত্য. অনুমান করুন আমার মন একবারে সত্যিকারের পুনরাবৃত্তি ব্যবহার করতে সক্ষম হয়ে
উঠেছে

1

ব্যবহারকারী 121196 এর এবং গ্রীন বেরেটের selectএস এবং <pre>এস এর উত্তরের ভিত্তিতে , আমার পক্ষে কাজ করা একমাত্র সমাধান হ'ল:

org.jsoup.nodes.Element elementWithHtml = ....
elementWithHtml.select("br").append("<pre>\n</pre>");
elementWithHtml.select("p").prepend("<pre>\n\n</pre>");
elementWithHtml.text();
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.