Удалить HTML-теги из строки

Есть ли хороший способ удалить HTML из строки Java? Простое регулярное выражение типа

 replaceAll("\\<.*?>","") 

будет работать, но такие вещи, как &amp; не будет преобразован правильно, и не-HTML между двумя угловыми скобками будут удалены (т.е. .*? в регулярном выражении исчезнет).

35 ответов

Решение

Используйте анализатор HTML вместо регулярных выражений. Это очень просто с Джоупом.

public static String html2text(String html) {
    return Jsoup.parse(html).text();
}

Jsoup также поддерживает удаление тегов HTML из настраиваемого белого списка, что очень полезно, если вы хотите разрешить только <b>, <i> а также <u>,

Смотрите также:

Если вы пишете для Android, вы можете сделать это...

android.text.Html.fromHtml(instruction).toString()

Если пользователь вводит <b>hey!</b>Вы хотите отобразить <b>hey!</b> или же hey!? Если первое, избегайте меньше чем, и закодируйте амперсанды html (и, возможно, кавычки), и все в порядке. Модификация вашего кода для реализации второго варианта будет:

replaceAll("\\<[^>]*>","")

но вы столкнетесь с проблемами, если пользователь введет что-то неправильно <bhey!</b>,

Вы также можете проверить JTidy, который проанализирует "грязный" ввод html и даст вам возможность удалить теги, сохраняя текст.

Проблема с попыткой лишить html заключается в том, что в браузерах есть очень мягкие парсеры, более мягкие, чем любая библиотека, которую вы можете найти, поэтому даже если вы приложите все усилия, чтобы удалить все теги (используя метод замены, описанный выше, библиотеку DOM или JTidy) Вам все равно нужно будет убедиться, что все оставшиеся специальные символы HTML кодированы, чтобы обеспечить безопасность вывода.

Другой способ - использовать javax.swing.text.html.HTMLEditorKit для извлечения текста.

import java.io.*;
import javax.swing.text.html.*;
import javax.swing.text.html.parser.*;

public class Html2Text extends HTMLEditorKit.ParserCallback {
    StringBuffer s;

    public Html2Text() {
    }

    public void parse(Reader in) throws IOException {
        s = new StringBuffer();
        ParserDelegator delegator = new ParserDelegator();
        // the third parameter is TRUE to ignore charset directive
        delegator.parse(in, this, Boolean.TRUE);
    }

    public void handleText(char[] text, int pos) {
        s.append(text);
    }

    public String getText() {
        return s.toString();
    }

    public static void main(String[] args) {
        try {
            // the HTML to convert
            FileReader in = new FileReader("java-new.html");
            Html2Text parser = new Html2Text();
            parser.parse(in);
            in.close();
            System.out.println(parser.getText());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

ref: удалить теги HTML из файла, чтобы извлечь только текст

Я думаю, что самый простой способ фильтрации HTML-тегов:

private static final Pattern REMOVE_TAGS = Pattern.compile("<.+?>");

public static String removeTags(String string) {
    if (string == null || string.length() == 0) {
        return string;
    }

    Matcher m = REMOVE_TAGS.matcher(string);
    return m.replaceAll("");
}

На Android попробуйте это:

String result = Html.fromHtml(html).toString();

Также очень просто использовать Jericho, и вы можете сохранить часть форматирования (разрывы строк и ссылки, например).

    Source htmlSource = new Source(htmlText);
    Segment htmlSeg = new Segment(htmlSource, 0, htmlSource.length());
    Renderer htmlRend = new Renderer(htmlSeg);
    System.out.println(htmlRend.toString());

Принятый ответ делать просто Jsoup.parse(html).text() имеет 2 потенциальных проблемы (с JSoup 1.7.3):

  • Удаляет разрывы строк из текста
  • Конвертирует текст &lt;script&gt; в <script>

Если вы используете это для защиты от XSS, это немного раздражает. Вот мой лучший пример улучшенного решения с использованием JSoup и Apache StringEscapeUtils:

// breaks multi-level of escaping, preventing &amp;lt;script&amp;gt; to be rendered as <script>
String replace = input.replace("&amp;", "");
// decode any encoded html, preventing &lt;script&gt; to be rendered as <script>
String html = StringEscapeUtils.unescapeHtml(replace);
// remove all html tags, but maintain line breaks
String clean = Jsoup.clean(html, "", Whitelist.none(), new Document.OutputSettings().prettyPrint(false));
// decode html again to convert character entities back into text
return StringEscapeUtils.unescapeHtml(clean);

Обратите внимание, что последний шаг, потому что мне нужно использовать вывод в виде простого текста. Если вам нужен только вывод HTML, вы сможете удалить его.

И вот несколько тестов (входные и выходные данные):

{"regular string", "regular string"},
{"<a href=\"link\">A link</a>", "A link"},
{"<script src=\"http://evil.url.com\"/>", ""},
{"&lt;script&gt;", ""},
{"&amp;lt;script&amp;gt;", "lt;scriptgt;"}, // best effort
{"\" ' > < \n \\ é å à ü and & preserved", "\" ' > < \n \\ é å à ü and & preserved"}

Если вы найдете способ сделать это лучше, пожалуйста, дайте мне знать.

Экранирование HTML действительно трудно сделать правильно - я бы определенно предложил использовать библиотечный код, чтобы сделать это, так как он намного более тонкий, чем вы думаете. Проверьте Apache StringEscapeUtils для довольно хорошей библиотеки для обработки этого в Java.

Это должно работать -

использовать этот

  text.replaceAll('<.*?>' , " ") -> This will replace all the html tags with a space.

и это

  text.replaceAll('&.*?;' , "")-> this will replace all the tags which starts with "&" and ends with ";" like &nbsp;, &amp;, &gt; etc.

Вы можете просто использовать HTML-фильтр Android по умолчанию

    public String htmlToStringFilter(String html){

    return Html.fromHtml(textToFilter).toString();

    }

Приведенный выше метод вернет отфильтрованную строку HTML для вашего ввода.

Вы можете заменить <br/> а также </p> теги с символами новой строки перед удалением HTML, чтобы он не стал неразборчивым беспорядком, как предполагает Тим.

Единственный способ, с помощью которого я могу удалить теги HTML, но оставить не-HTML между угловыми скобками, - это проверить список тегов HTML. Что-то в этом роде...

replaceAll("\\<[\s]*tag[^>]*>","")

Затем HTML-декодирование специальных символов, таких как &amp;, Результат не следует считать санированным.

Вот еще один вариант того, как заменить все (HTML-теги | HTML-объекты | Пустое пространство в содержимом HTML)

content.replaceAll("(<.*?>)|(&.*?;)|([ ]{2,})", ""); где содержимое является строкой.

В качестве альтернативы можно использовать HtmlCleaner:

private CharSequence removeHtmlFrom(String html) {
    return new HtmlCleaner().clean(html).getText();
}

Вот чуть более детальное обновление, чтобы попытаться обработать некоторое форматирование для разрывов и списков. Я использовал выход Amaya в качестве руководства.

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.Stack;
import java.util.logging.Logger;

import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.parser.ParserDelegator;

public class HTML2Text extends HTMLEditorKit.ParserCallback {
    private static final Logger log = Logger
            .getLogger(Logger.GLOBAL_LOGGER_NAME);

    private StringBuffer stringBuffer;

    private Stack<IndexType> indentStack;

    public static class IndexType {
        public String type;
        public int counter; // used for ordered lists

        public IndexType(String type) {
            this.type = type;
            counter = 0;
        }
    }

    public HTML2Text() {
        stringBuffer = new StringBuffer();
        indentStack = new Stack<IndexType>();
    }

    public static String convert(String html) {
        HTML2Text parser = new HTML2Text();
        Reader in = new StringReader(html);
        try {
            // the HTML to convert
            parser.parse(in);
        } catch (Exception e) {
            log.severe(e.getMessage());
        } finally {
            try {
                in.close();
            } catch (IOException ioe) {
                // this should never happen
            }
        }
        return parser.getText();
    }

    public void parse(Reader in) throws IOException {
        ParserDelegator delegator = new ParserDelegator();
        // the third parameter is TRUE to ignore charset directive
        delegator.parse(in, this, Boolean.TRUE);
    }

    public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
        log.info("StartTag:" + t.toString());
        if (t.toString().equals("p")) {
            if (stringBuffer.length() > 0
                    && !stringBuffer.substring(stringBuffer.length() - 1)
                            .equals("\n")) {
                newLine();
            }
            newLine();
        } else if (t.toString().equals("ol")) {
            indentStack.push(new IndexType("ol"));
            newLine();
        } else if (t.toString().equals("ul")) {
            indentStack.push(new IndexType("ul"));
            newLine();
        } else if (t.toString().equals("li")) {
            IndexType parent = indentStack.peek();
            if (parent.type.equals("ol")) {
                String numberString = "" + (++parent.counter) + ".";
                stringBuffer.append(numberString);
                for (int i = 0; i < (4 - numberString.length()); i++) {
                    stringBuffer.append(" ");
                }
            } else {
                stringBuffer.append("*   ");
            }
            indentStack.push(new IndexType("li"));
        } else if (t.toString().equals("dl")) {
            newLine();
        } else if (t.toString().equals("dt")) {
            newLine();
        } else if (t.toString().equals("dd")) {
            indentStack.push(new IndexType("dd"));
            newLine();
        }
    }

    private void newLine() {
        stringBuffer.append("\n");
        for (int i = 0; i < indentStack.size(); i++) {
            stringBuffer.append("    ");
        }
    }

    public void handleEndTag(HTML.Tag t, int pos) {
        log.info("EndTag:" + t.toString());
        if (t.toString().equals("p")) {
            newLine();
        } else if (t.toString().equals("ol")) {
            indentStack.pop();
            ;
            newLine();
        } else if (t.toString().equals("ul")) {
            indentStack.pop();
            ;
            newLine();
        } else if (t.toString().equals("li")) {
            indentStack.pop();
            ;
            newLine();
        } else if (t.toString().equals("dd")) {
            indentStack.pop();
            ;
        }
    }

    public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
        log.info("SimpleTag:" + t.toString());
        if (t.toString().equals("br")) {
            newLine();
        }
    }

    public void handleText(char[] text, int pos) {
        log.info("Text:" + new String(text));
        stringBuffer.append(text);
    }

    public String getText() {
        return stringBuffer.toString();
    }

    public static void main(String args[]) {
        String html = "<html><body><p>paragraph at start</p>hello<br />What is happening?<p>this is a<br />mutiline paragraph</p><ol>  <li>This</li>  <li>is</li>  <li>an</li>  <li>ordered</li>  <li>list    <p>with</p>    <ul>      <li>another</li>      <li>list        <dl>          <dt>This</dt>          <dt>is</dt>            <dd>sdasd</dd>            <dd>sdasda</dd>            <dd>asda              <p>aasdas</p>            </dd>            <dd>sdada</dd>          <dt>fsdfsdfsd</dt>        </dl>        <dl>          <dt>vbcvcvbcvb</dt>          <dt>cvbcvbc</dt>            <dd>vbcbcvbcvb</dd>          <dt>cvbcv</dt>          <dt></dt>        </dl>        <dl>          <dt></dt>        </dl></li>      <li>cool</li>    </ul>    <p>stuff</p>  </li>  <li>cool</li></ol><p></p></body></html>";
        System.out.println(convert(html));
    }
}

Принятый ответ не сработал для указанного мной теста: результат "a c" равен "a b or b > c".

Итак, я использовал TagSoup вместо этого. Вот пример, который сработал для моего тестового примера (и нескольких других):

import java.io.IOException;
import java.io.StringReader;
import java.util.logging.Logger;

import org.ccil.cowan.tagsoup.Parser;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

/**
 * Take HTML and give back the text part while dropping the HTML tags.
 *
 * There is some risk that using TagSoup means we'll permute non-HTML text.
 * However, it seems to work the best so far in test cases.
 *
 * @author dan
 * @see <a href="http://home.ccil.org/~cowan/XML/tagsoup/">TagSoup</a> 
 */
public class Html2Text2 implements ContentHandler {
private StringBuffer sb;

public Html2Text2() {
}

public void parse(String str) throws IOException, SAXException {
    XMLReader reader = new Parser();
    reader.setContentHandler(this);
    sb = new StringBuffer();
    reader.parse(new InputSource(new StringReader(str)));
}

public String getText() {
    return sb.toString();
}

@Override
public void characters(char[] ch, int start, int length)
    throws SAXException {
    for (int idx = 0; idx < length; idx++) {
    sb.append(ch[idx+start]);
    }
}

@Override
public void ignorableWhitespace(char[] ch, int start, int length)
    throws SAXException {
    sb.append(ch);
}

// The methods below do not contribute to the text
@Override
public void endDocument() throws SAXException {
}

@Override
public void endElement(String uri, String localName, String qName)
    throws SAXException {
}

@Override
public void endPrefixMapping(String prefix) throws SAXException {
}


@Override
public void processingInstruction(String target, String data)
    throws SAXException {
}

@Override
public void setDocumentLocator(Locator locator) {
}

@Override
public void skippedEntity(String name) throws SAXException {
}

@Override
public void startDocument() throws SAXException {
}

@Override
public void startElement(String uri, String localName, String qName,
    Attributes atts) throws SAXException {
}

@Override
public void startPrefixMapping(String prefix, String uri)
    throws SAXException {
}
}

Использование Html.fromHtml

HTML- теги

<a href=”…”> <b>,  <big>, <blockquote>, <br>, <cite>, <dfn>
<div align=”…”>,  <em>, <font size=”…” color=”…” face=”…”>
<h1>,  <h2>, <h3>, <h4>,  <h5>, <h6>
<i>, <p>, <small>
<strike>,  <strong>, <sub>, <sup>, <tt>, <u>

В соответствии с официальной документацией Android любые теги в HTML будут отображаться как общая замещающая строка, которую ваша программа может затем пройти и заменить на реальные строки.

Html.formHtml метод принимает Html.TagHandler и Html.ImageGetter в качестве аргументов, а также текст для анализа.

пример

String Str_Html=" <p>This is about me text that the user can put into their profile</p> ";

затем

Your_TextView_Obj.setText(Html.fromHtml(Str_Html).toString());

Выход

Это обо мне текст, который пользователь может поместить в свой профиль

Еще один способ - использовать класс com.google.gdata.util.common.html.HtmlToText, например

MyWriter.toConsole(HtmlToText.htmlToPlainText(htmlResponse));

Это не пуленепробиваемый код, и когда я запускаю его в записях википедии, я также получаю информацию о стиле. Однако я считаю, что для небольших / простых работ это будет эффективно.

Похоже, вы хотите перейти от HTML к простому тексту.
Если это так, посмотрите на www.htmlparser.org. Вот пример, который удаляет все теги из html-файла, найденного по URL.
Он использует org.htmlparser.beans.StringBean.

static public String getUrlContentsAsText(String url) {
    String content = "";
    StringBean stringBean = new StringBean();
    stringBean.setURL(url);
    content = stringBean.getStrings();
    return content;
}

Я знаю, что это старый, но я просто работал над проектом, который требовал от меня фильтрации HTML, и это работало нормально:

noHTMLString.replaceAll("\\&.*?\\;", "");

вместо этого:

html = html.replaceAll("&nbsp;","");
html = html.replaceAll("&amp;"."");

Вот еще один способ сделать это:

public static String removeHTML(String input) {
    int i = 0;
    String[] str = input.split("");

    String s = "";
    boolean inTag = false;

    for (i = input.indexOf("<"); i < input.indexOf(">"); i++) {
        inTag = true;
    }
    if (!inTag) {
        for (i = 0; i < str.length; i++) {
            s = s + str[i];
        }
    }
    return s;
}

Можно также использовать Apache Tika для этой цели. По умолчанию он сохраняет пробелы из раздетого html, которые могут быть желательны в определенных ситуациях:

InputStream htmlInputStream = ..
HtmlParser htmlParser = new HtmlParser();
HtmlContentHandler htmlContentHandler = new HtmlContentHandler();
htmlParser.parse(htmlInputStream, htmlContentHandler, new Metadata())
System.out.println(htmlContentHandler.getBodyText().trim())

Попробуйте это для javascript:

const strippedString = htmlString.replace(/(<([^>]+)>)/gi, "");
console.log(strippedString);

Вы можете использовать этот метод для удаления тегов HTML из строки,

      public static String stripHtmlTags(String html) {

    return html.replaceAll("<.*?>", "");

}

Например: classeString.replaceAll ("\<(/? [^ \>] +) \>", "\").replaceAll ("\ s +", "").trim ()

Один из способов сохранить информацию о новой строке с помощью JSoup - это предвосхитить все теги новой строки какой-нибудь фиктивной строкой, выполнить JSoup и заменить фиктивную строку на "\n".

String html = "<p>Line one</p><p>Line two</p>Line three<br/>etc.";
String NEW_LINE_MARK = "NEWLINESTART1234567890NEWLINEEND";
for (String tag: new String[]{"</p>","<br/>","</h1>","</h2>","</h3>","</h4>","</h5>","</h6>","</li>"}) {
    html = html.replace(tag, NEW_LINE_MARK+tag);
}

String text = Jsoup.parse(html).text();

text = text.replace(NEW_LINE_MARK + " ", "\n\n");
text = text.replace(NEW_LINE_MARK, "\n\n");

Иногда строка html поступает из xml с такими &lt. При использовании Jsoup нам нужно его проанализировать, а затем очистить.

Document doc = Jsoup.parse(htmlstrl);
Whitelist wl = Whitelist.none();
String plain = Jsoup.clean(doc.text(), wl);

Пока только используя Jsoup.parse(htmlstrl).text() не может удалить теги.

Я часто обнаруживаю, что мне нужно только вырезать комментарии и элементы скрипта. Это надежно работало для меня в течение 15 лет и может быть легко расширено для обработки любого имени элемента в HTML или XML:

// delete all comments
response = response.replaceAll("<!--[^>]*-->", "");
// delete all script elements
response = response.replaceAll("<(script|SCRIPT)[^+]*?>[^>]*?<(/script|SCRIPT)>", "");

Вы можете использовать этот код для удаления тегов HTML, включая разрывы строк.

      function remove_html_tags(html) {
    html = html.replace(/<div>/g, "").replace(/<\/div>/g, "<br>");
    html = html.replace(/<br>/g, "$br$");
    html = html.replace(/(?:\r\n|\r|\n)/g, '$br$');
    var tmp = document.createElement("DIV");
    tmp.innerHTML = html;
    html = tmp.textContent || tmp.innerText;
    html = html.replace(/\$br\$/g, "\n");
    return html;
}

Я знаю, что прошло некоторое время с тех пор, как этот вопрос был задан, но я нашел другое решение, это то, что сработало для меня:

Pattern REMOVE_TAGS = Pattern.compile("<.+?>");
    Source source= new Source(htmlAsString);
 Matcher m = REMOVE_TAGS.matcher(sourceStep.getTextExtractor().toString());
                        String clearedHtml= m.replaceAll("");
Другие вопросы по тегам