Entfernen Sie HTML von Zeichenfolgen in Python


268
from mechanize import Browser
br = Browser()
br.open('http://somewebpage')
html = br.response().readlines()
for line in html:
  print line

Beim Drucken einer Zeile in einer HTML-Datei versuche ich, nur den Inhalt jedes HTML-Elements und nicht die Formatierung selbst anzuzeigen. Wenn es findet '<a href="whatever.com">some text</a>', druckt es nur "Text", '<b>hello</b>'"Hallo" usw. Wie würde man das machen?


16
Eine wichtige Überlegung ist der Umgang mit HTML-Entitäten (z &amp;. B. ). Sie können entweder 1) sie zusammen mit den Tags entfernen (oft unerwünscht und unnötig, da sie einfachem Text entsprechen), 2) sie unverändert lassen (eine geeignete Lösung, wenn der gestrippte Text direkt in einen HTML-Kontext zurückkehrt) oder 3) ) dekodieren Sie sie in einfachen Text (wenn der gestrippte Text in eine Datenbank oder einen anderen Nicht-HTML-Kontext verschoben wird oder wenn Ihr Webframework automatisch HTML-Escapezeichen für Sie ausführt).
Søren Løvborg

2
für @ SørenLøvborg Punkt 2): stackoverflow.com/questions/753052/…
Robert

2
Die Top-Antwort hier, die vom Django-Projekt bis März 2014 verwendet wurde, hat sich als unsicher gegenüber Cross-Site-Scripting erwiesen. Ein Beispiel finden Sie unter diesem Link. Ich empfehle die Verwendung von Bleach.clean (), Markupsafe's Striptags oder RECENT Djangos Strip_tags.
Rescdsk

Antworten:


418

Ich habe diese Funktion immer verwendet, um HTML-Tags zu entfernen, da nur die Python-Stdlib erforderlich ist:

Für Python 3:

from io import StringIO
from html.parser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        super().__init__()
        self.reset()
        self.strict = False
        self.convert_charrefs= True
        self.text = StringIO()
    def handle_data(self, d):
        self.text.write(d)
    def get_data(self):
        return self.text.getvalue()

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()

Für Python 2:

from HTMLParser import HTMLParser
from StringIO import StringIO

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.text = StringIO()
    def handle_data(self, d):
        self.text.write(d)
    def get_data(self):
        return self.text.getvalue()

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()

3
Zwei Jahre später steht das gleiche Problem an, und dies ist eine weitaus elegantere Lösung. Die einzige Änderung, die ich vorgenommen habe, war, self.fed als Liste zurückzugeben, anstatt sie zu verknüpfen, damit ich den Inhalt des Elements durchgehen kann.
Regie

47
Beachten Sie, dass dadurch sowohl HTML-Entitäten (z. B. &amp;) als auch Tags entfernt werden.
Søren Løvborg

30
@surya Ich bin sicher, Sie haben dies gesehen
tkone

8
Danke für die tolle Antwort. Für diejenigen unter Ihnen, die neuere Versionen von Python (3.2+) verwenden, ist zu beachten, dass Sie die __init__Funktion der übergeordneten Klasse aufrufen müssen. Siehe hier: stackoverflow.com/questions/11061058/… .
Pseudoramble

10
Um die HTML-Entitäten (in Unicode konvertiert) beizubehalten , habe ich zwei Zeilen hinzugefügt: parser = HTMLParser()und html = parser.unescape(html)am Anfang der Funktion strip_tags.
James Doepp - Pihentagyu

156

Ich habe nicht viel über die Fälle nachgedacht, die es vermissen wird, aber Sie können einen einfachen regulären Ausdruck machen:

re.sub('<[^<]+?>', '', text)

Für diejenigen, die Regex nicht verstehen, wird nach einer Zeichenfolge gesucht, <...>bei der der innere Inhalt aus einem oder mehreren ( +) Zeichen besteht, die kein a sind <. Dies ?bedeutet, dass es mit der kleinsten Zeichenfolge übereinstimmt, die es finden kann. Zum Beispiel <p>Hello</p>wird es übereinstimmen <'p>und </p>separat mit dem ?. Ohne sie wird es mit der gesamten Zeichenfolge übereinstimmen <..Hello..>.

Wenn <in HTML (z. B. 2 < 3) kein Tag angezeigt wird , sollte es &...ohnehin als Escape-Sequenz geschrieben werden, damit dies ^<möglicherweise nicht erforderlich ist.


10
Genau so macht es Djangos strip_tags .
Bluu

10
Beachten Sie, dass dadurch HTML-Entitäten (z. B. &amp;) in der Ausgabe unverändert bleiben.
Søren Løvborg

35
Man kann diese Methode immer noch mit so etwas

19
TUN SIE ES NICHT AUF DIESE WEISE! Wie @Julio Garcia sagt, ist es NICHT SICHER!
Rescdsk

18
Leute, verwechseln Sie nicht HTML-Stripping und HTML-Desinfektion. Ja, bei fehlerhaften oder böswilligen Eingaben kann diese Antwort eine Ausgabe mit HTML-Tags erzeugen. Es ist immer noch ein absolut gültiger Ansatz, um HTML-Tags zu entfernen. Das Entfernen von HTML-Tags ist jedoch kein gültiger Ersatz für eine ordnungsgemäße HTML-Bereinigung. Die Regel ist nicht schwer: Jedes Mal, wenn Sie eine Nur-Text-Zeichenfolge in die HTML-Ausgabe einfügen, sollten Sie sie immer mit HTML maskieren (mit cgi.escape(s, True)), auch wenn Sie "wissen", dass sie kein HTML enthält (z. B. weil Sie HTML-Inhalte entfernt haben). . Dies ist jedoch nicht das, wonach OP gefragt hat.
Søren Løvborg

76

Sie können die BeautifulSoup- get_text()Funktion verwenden.

from bs4 import BeautifulSoup

html_str = '''
<td><a href="http://www.fakewebsite.com">Please can you strip me?</a>
<br/><a href="http://www.fakewebsite.com">I am waiting....</a>
</td>
'''
soup = BeautifulSoup(html_str)

print(soup.get_text()) 
#or via attribute of Soup Object: print(soup.text)

Es ist ratsam, den Parser explizit anzugeben , z. B. BeautifulSoup(html_str, features="html.parser")damit die Ausgabe reproduzierbar ist.


32

Kurzfassung!

import re, cgi
tag_re = re.compile(r'(<!--.*?-->|<[^>]*>)')

# Remove well-formed tags, fixing mistakes by legitimate users
no_tags = tag_re.sub('', user_input)

# Clean up anything else by escaping
ready_for_web = cgi.escape(no_tags)

Regex-Quelle: MarkupSafe . Ihre Version verarbeitet auch HTML-Entitäten, während diese schnelle dies nicht tut.

Warum kann ich die Tags nicht einfach entfernen und lassen?

Es ist eine Sache, Menschen von <i>italicizing</i>Dingen fernzuhalten , ohne dass sie iherumschweben. Aber es ist eine andere Sache, willkürliche Eingaben zu machen und sie völlig harmlos zu machen. Bei den meisten Techniken auf dieser Seite <!--bleiben Dinge wie nicht geschlossene Kommentare ( ) und spitze Klammern, die nicht Teil von tags ( blah <<<><blah) sind, intakt. Die HTMLParser-Version kann sogar vollständige Tags hinterlassen, wenn sie sich in einem nicht geschlossenen Kommentar befinden.

Was ist, wenn Ihre Vorlage ist {{ firstname }} {{ lastname }}? firstname = '<a'und lastname = 'href="http://evil.com/">'wird von jedem Tag-Stripper auf dieser Seite (außer @Medeiros!) durchgelassen, da es sich nicht um vollständige Tags handelt. Das Entfernen normaler HTML-Tags reicht nicht aus.

Django's strip_tags, eine verbesserte (siehe nächste Überschrift) Version der Top-Antwort auf diese Frage, gibt die folgende Warnung:

Es wird absolut KEINE Garantie dafür gegeben, dass die resultierende Zeichenfolge HTML-sicher ist. Markieren Sie das Ergebnis eines strip_tagsAnrufs also NIEMALS als sicher , ohne ihn zuerst zu umgehen, z. B. mit escape().

Folgen Sie ihrem Rat!

Um Tags mit HTMLParser zu entfernen, müssen Sie es mehrmals ausführen.

Es ist einfach, die beste Antwort auf diese Frage zu umgehen.

Schauen Sie sich diese Zeichenfolge an ( Quelle und Diskussion ):

<img<!-- --> src=x onerror=alert(1);//><!-- -->

Wenn HTMLParser es zum ersten Mal sieht, kann es nicht erkennen, dass <img...>es sich um ein Tag handelt. Es sieht kaputt aus, sodass HTMLParser es nicht loswird. Es nimmt nur das heraus <!-- comments -->und lässt dich mit

<img src=x onerror=alert(1);//>

Dieses Problem wurde dem Django-Projekt im März 2014 mitgeteilt. Ihr altes Problem entsprach im strip_tagsWesentlichen der Top-Antwort auf diese Frage. Ihre neue Version führt es im Grunde genommen in einer Schleife aus, bis das erneute Ausführen die Zeichenfolge nicht ändert:

# _strip_once runs HTMLParser once, pulling out just the text of all the nodes.

def strip_tags(value):
    """Returns the given HTML with all tags stripped."""
    # Note: in typical case this loop executes _strip_once once. Loop condition
    # is redundant, but helps to reduce number of executions of _strip_once.
    while '<' in value and '>' in value:
        new_value = _strip_once(value)
        if len(new_value) >= len(value):
            # _strip_once was not able to detect more tags
            break
        value = new_value
    return value

Natürlich ist nichts davon ein Problem, wenn Sie immer dem Ergebnis von entkommen strip_tags().

Update 19. März 2015 : In den Django-Versionen vor 1.4.20, 1.6.11, 1.7.7 und 1.8c1 ist ein Fehler aufgetreten. Diese Versionen könnten in der Funktion strip_tags () eine Endlosschleife eingeben. Die feste Version ist oben wiedergegeben. Weitere Details hier .

Gute Dinge zum Kopieren oder Verwenden

Mein Beispielcode verarbeitet keine HTML-Entitäten - die Paketversionen Django und MarkupSafe tun dies.

Mein Beispielcode stammt aus der hervorragenden MarkupSafe- Bibliothek zur Verhinderung von Cross-Site-Scripting. Es ist bequem und schnell (mit C-Beschleunigungen auf die native Python-Version). Es ist in der Google App Engine enthalten und wird von Jinja2 (2.7 und höher) , Mako, Pylons und anderen verwendet. Es funktioniert problemlos mit Django-Vorlagen aus Django 1.7.

Djangos strip_tags und andere HTML-Dienstprogramme aus einer neueren Version sind gut, aber ich finde sie weniger praktisch als MarkupSafe. Sie sind ziemlich eigenständig. Sie können aus dieser Datei kopieren, was Sie benötigen .

Wenn Sie fast alle Tags entfernen müssen, ist die Bleach- Bibliothek gut. Sie können Regeln wie "Meine Benutzer können Dinge kursiv schreiben, aber sie können keine Iframes erstellen" erzwingen lassen.

Verstehen Sie die Eigenschaften Ihres Tag Strippers! Führen Sie Fuzz-Tests durch! Hier ist der Code, mit dem ich nach dieser Antwort gesucht habe.

verlegener Hinweis - Bei der Frage selbst geht es um das Drucken auf der Konsole. Dies ist jedoch das beste Google-Ergebnis für "Python-Strip-HTML von Zeichenfolge". Aus diesem Grund bezieht sich diese Antwort zu 99% auf das Web.


Mein Beispielcode "Alternative letzte Zeile" behandelt keine HTML-Entitäten - wie schlimm ist das?
Rescdsk

Ich analysiere nur einen kleinen Teil von HTML ohne spezielle Tags, und Ihre Kurzversion macht den Job sehr gut. Danke für das Teilen!
Tbolender

31

Ich brauchte eine Möglichkeit, Tags zu entfernen und HTML-Entitäten in einfachen Text zu dekodieren. Die folgende Lösung basiert auf Eloffs Antwort (die ich nicht verwenden konnte, weil sie Entitäten entfernt).

from HTMLParser import HTMLParser
import htmlentitydefs

class HTMLTextExtractor(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.result = [ ]

    def handle_data(self, d):
        self.result.append(d)

    def handle_charref(self, number):
        codepoint = int(number[1:], 16) if number[0] in (u'x', u'X') else int(number)
        self.result.append(unichr(codepoint))

    def handle_entityref(self, name):
        codepoint = htmlentitydefs.name2codepoint[name]
        self.result.append(unichr(codepoint))

    def get_text(self):
        return u''.join(self.result)

def html_to_text(html):
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()

Ein kurzer Test:

html = u'<a href="#">Demo <em>(&not; \u0394&#x03b7;&#956;&#x03CE;)</em></a>'
print repr(html_to_text(html))

Ergebnis:

u'Demo (\xac \u0394\u03b7\u03bc\u03ce)'

Fehlerbehandlung:

  • Eine ungültige HTML-Struktur kann einen HTMLParseError verursachen .
  • Ungültige benannte HTML-Entitäten (z. B. &#apos;die in XML und XHTML gültig sind, jedoch kein einfaches HTML) verursachen eine ValueErrorAusnahme.
  • Numerische HTML-Entitäten, die Codepunkte außerhalb des von Python akzeptierten Unicode-Bereichs angeben (z. B. auf einigen Systemen Zeichen außerhalb der mehrsprachigen Grundebene ), verursachen eine ValueErrorAusnahme.

Sicherheitshinweis: Verwechseln Sie HTML-Stripping (Konvertieren von HTML in einfachen Text) nicht mit HTML-Bereinigung (Konvertieren von einfachem Text in HTML). Diese Antwort entfernt HTML und dekodiert Entitäten in einfachen Text - dies macht die Verwendung des Ergebnisses in einem HTML-Kontext nicht sicher.

Beispiel: &lt;script&gt;alert("Hello");&lt;/script&gt;wird in konvertiert <script>alert("Hello");</script>, was zu 100% korrekt ist, aber offensichtlich nicht ausreicht, wenn der resultierende Klartext unverändert in eine HTML-Seite eingefügt wird.

Die Regel ist nicht schwer: Jedes Mal, wenn Sie eine Nur-Text-Zeichenfolge in die HTML-Ausgabe einfügen, sollten Sie sie immer mit HTML maskieren (mit cgi.escape(s, True)), auch wenn Sie "wissen", dass sie kein HTML enthält (z. B. weil Sie HTML-Inhalte entfernt haben). .

(Das OP fragte jedoch nach dem Drucken des Ergebnisses auf der Konsole. In diesem Fall ist kein HTML-Escape erforderlich.)

Python 3.4+ Version: (mit Doctest!)

import html.parser

class HTMLTextExtractor(html.parser.HTMLParser):
    def __init__(self):
        super(HTMLTextExtractor, self).__init__()
        self.result = [ ]

    def handle_data(self, d):
        self.result.append(d)

    def get_text(self):
        return ''.join(self.result)

def html_to_text(html):
    """Converts HTML to plain text (stripping tags and converting entities).
    >>> html_to_text('<a href="#">Demo<!--...--> <em>(&not; \u0394&#x03b7;&#956;&#x03CE;)</em></a>')
    'Demo (\xac \u0394\u03b7\u03bc\u03ce)'

    "Plain text" doesn't mean result can safely be used as-is in HTML.
    >>> html_to_text('&lt;script&gt;alert("Hello");&lt;/script&gt;')
    '<script>alert("Hello");</script>'

    Always use html.escape to sanitize text before using in an HTML context!

    HTMLParser will do its best to make sense of invalid HTML.
    >>> html_to_text('x < y &lt z <!--b')
    'x < y < z '

    Unrecognized named entities are included as-is. '&apos;' is recognized,
    despite being XML only.
    >>> html_to_text('&nosuchentity; &apos; ')
    "&nosuchentity; ' "
    """
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()

Beachten Sie, dass HTMLParser in Python 3 verbessert wurde (was weniger Code und eine bessere Fehlerbehandlung bedeutet).


18

Es gibt einen einfachen Weg dazu:

def remove_html_markup(s):
    tag = False
    quote = False
    out = ""

    for c in s:
            if c == '<' and not quote:
                tag = True
            elif c == '>' and not quote:
                tag = False
            elif (c == '"' or c == "'") and tag:
                quote = not quote
            elif not tag:
                out = out + c

    return out

Die Idee wird hier erklärt: http://youtu.be/2tu9LTDujbw

Sie können es hier sehen: http://youtu.be/HPkNPcYed9M?t=35s

PS - Wenn Sie an der Klasse interessiert sind (über intelligentes Debuggen mit Python), gebe ich Ihnen einen Link: http://www.udacity.com/overview/Course/cs259/CourseRev/1 . Es ist kostenlos!

Bitte! :) :)


2
Ich frage mich, warum diese Antwort gerade abgelehnt wurde. Es ist eine einfache Möglichkeit, das Problem ohne Bibliothek zu lösen. Nur reine Python und es funktioniert wie durch die Links gezeigt.
Medeiros

2
Wahrscheinlich bevorzugen die Leute Bibliotheken, um ihnen Sicherheit zu geben. Ich habe Ihren Code getestet und bestanden, und ich bevorzuge immer kleinen Code, den ich verstehe, als eine Bibliothek zu verwenden und davon auszugehen, dass er in Ordnung ist, bis ein Fehler auftritt. Für mich war es das, wonach ich gesucht habe und nochmals vielen Dank. In Bezug auf die Abstimmungen sollten Sie nicht in diese Denkweise geraten. Die Leute hier sollten sich um die Qualität und nicht um die Stimmen kümmern. In letzter Zeit ist SO zu einem Ort geworden, an dem jeder die Punkte und nicht das Wissen will.
Jimmy Kane

2
Das Problem bei dieser Lösung ist die Fehlerbehandlung. Zum Beispiel, wenn Sie <b class="o'>x</b>als Eingabefunktion Ausgänge geben x. Tatsächlich ist diese Eingabe jedoch ungültig. Ich denke, deshalb bevorzugen die Leute Bibliotheken.
Laltin

1
Es funktioniert auch mit diesem Eingang. Gerade getestet. Stellen Sie nur fest, dass Sie in diesen Bibliotheken ähnlichen Code finden. Es ist nicht sehr pythonisch, ich weiß. Sieht aus wie C- oder Java-Code. Ich denke, es ist effizient und kann leicht in eine andere Sprache portiert werden.
Medeiros

1
Einfach, pythonisch und scheint genauso gut oder besser zu funktionieren als alle anderen diskutierten Methoden. Es ist möglich, dass es bei schlecht geformtem HTML nicht funktioniert, aber das lässt sich nicht überwinden.
Derson

16

Wenn Sie HTML-Entitäten (dh &amp;) beibehalten müssen, habe ich Eloffs Antwort die Methode "handle_entityref" hinzugefügt .

from HTMLParser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
    def handle_data(self, d):
        self.fed.append(d)
    def handle_entityref(self, name):
        self.fed.append('&%s;' % name)
    def get_data(self):
        return ''.join(self.fed)

def html_to_text(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()

13

Wenn Sie alle HTML-Tags entfernen möchten, ist die Verwendung von BeautifulSoup der einfachste Weg, den ich gefunden habe:

from bs4 import BeautifulSoup  # Or from BeautifulSoup import BeautifulSoup

def stripHtmlTags(htmlTxt):
    if htmlTxt is None:
            return None
        else:
            return ''.join(BeautifulSoup(htmlTxt).findAll(text=True)) 

Ich habe den Code der akzeptierten Antwort ausprobiert, aber "RuntimeError: Maximale Rekursionstiefe überschritten" wurde angezeigt, was mit dem obigen Codeblock nicht der Fall war.


1
Ich habe gerade Ihre Methode ausprobiert, weil sie sauberer zu sein scheint, funktioniert hat, na ja ... sie hat keine Eingabe-Tags entfernt!
kustomrtr

Ich finde, dass eine einfache Anwendung von BeautifulSoup ein Problem mit Leerzeichen hat : ''.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True)). Hier ist die Ausgabe "helloworld", während Sie wahrscheinlich möchten, dass es "Hallo Welt" ist. ' '.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True))hilft nicht, da es "er llo Welt" wird.
Finn Årup Nielsen

@kustomrtr, sorry meine Unwissenheit, was stecke ich in das Selbstargument? NameError: Name 'self' ist nicht definiert
Ian_De_Oliveira

@Ian_De_Oliveira Du kannst es entfernen, ich habe angenommen, es befindet sich in einer Klasse, aber es wird nicht benötigt. Ich habe auch die Antwort bearbeitet, um sie zu entfernen
Vasilis

@Ian_De_Oliveira Du kannst es entfernen, ich habe angenommen, es befindet sich in einer Klasse, aber es wird nicht benötigt. Ich habe auch die Antwort bearbeitet, um sie zu entfernen
Vasilis

9

Eine lxml.html- basierte Lösung (lxml ist eine native Bibliothek und daher viel schneller als jede reine Python-Lösung).

from lxml import html
from lxml.html.clean import clean_html

tree = html.fromstring("""<span class="item-summary">
                            Detailed answers to any questions you might have
                        </span>""")

print(clean_html(tree).strip())

# >>> Detailed answers to any questions you might have

Siehe auch http://lxml.de/lxmlhtml.html#cleaning-up-html, was genau der lxml.cleaner tut.

Wenn Sie vor der Konvertierung in Text mehr Kontrolle darüber benötigen, was genau bereinigt wird, können Sie den lxml-Reiniger explizit verwenden, indem Sie die gewünschten Optionen im Konstruktor übergeben, z.

cleaner = Cleaner(page_structure=True,
                  meta=True,
                  embedded=True,
                  links=True,
                  style=True,
                  processing_instructions=True,
                  inline_style=True,
                  scripts=True,
                  javascript=True,
                  comments=True,
                  frames=True,
                  forms=True,
                  annoying_tags=True,
                  remove_unknown_tags=True,
                  safe_attrs_only=True,
                  safe_attrs=frozenset(['src','color', 'href', 'title', 'class', 'name', 'id']),
                  remove_tags=('span', 'font', 'div')
                  )
sanitized_html = cleaner.clean_html(unsafe_html)

1
Ich habe AttributeError: 'HtmlElement' Objekt hat kein Attribut 'Strip'
aris

9

Hier ist eine einfache Lösung, die HTML-Tags entfernt und HTML-Entitäten basierend auf der erstaunlich schnellen lxmlBibliothek dekodiert :

from lxml import html

def strip_html(s):
    return str(html.fromstring(s).text_content())

strip_html('Ein <a href="">sch&ouml;ner</a> Text.')  # Output: Ein schöner Text.

3
Ab 2020 war dies der schnellste und beste Weg, um den Inhalt des HTML-Codes zu entfernen. Plus den Bonus der Handhabung der Dekodierung. Ideal für die Spracherkennung!
Dfabiano

text_content()kehrt zurück, lxml.etree._ElementUnicodeResultsodass Sie es möglicherweise zuerst in eine Zeichenfolge
umwandeln müssen

1
@ Susana Guter Punkt. Es scheint strfür Zeichenfolgenoperationen wie +und Indizierung automatisch umgewandelt zu werden []. Es wurde sowieso eine Besetzung für ein gutes Maß hinzugefügt.
Robin Dinse

7

Das Beautiful Soup-Paket erledigt dies sofort für Sie.

from bs4 import BeautifulSoup

soup = BeautifulSoup(html)
text = soup.get_text()
print(text)

3
Aus der Überprüfungswarteschlange: Darf ich Sie bitten, Ihrer Antwort einen weiteren Kontext hinzuzufügen. Nur-Code-Antworten sind schwer zu verstehen. Es wird sowohl dem Fragesteller als auch zukünftigen Lesern helfen, wenn Sie Ihrem Beitrag weitere Informationen hinzufügen können.
help-info.de

2

Hier ist meine Lösung für Python 3.

import html
import re

def html_to_txt(html_text):
    ## unescape html
    txt = html.unescape(html_text)
    tags = re.findall("<[^>]+>",txt)
    print("found tags: ")
    print(tags)
    for tag in tags:
        txt=txt.replace(tag,'')
    return txt

Ich bin mir nicht sicher, ob es perfekt ist, habe aber meinen Anwendungsfall gelöst und scheint einfach zu sein.


2

Sie können entweder einen anderen HTML-Parser ( wie lxml oder Beautiful Soup ) verwenden, der Funktionen zum Extrahieren von nur Text bietet. Sie können auch einen regulären Ausdruck für Ihre Zeilenzeichenfolge ausführen, mit dem die Tags entfernt werden. Weitere Informationen finden Sie in den Python-Dokumenten .


1
Amk Link ist tot. Hast du eine Alternative?

2
Die Python-Website hat jetzt gute Anleitungen
Jason Coon

5
In lxml:lxml.html.fromstring(s).text_content()
Bluu

1
Bluus Beispiel mit lxml dekodiert HTML-Entitäten (z &amp;. B. ) in Text.
Søren Løvborg

1

Ich habe Eloffs Antwort erfolgreich für Python 3.1 verwendet [vielen Dank!].

Ich habe ein Upgrade auf Python 3.2.3 durchgeführt und bin auf Fehler gestoßen.

Die Lösung, die hier dank des Antwortenden Thomas K bereitgestellt wird , besteht darin, super().__init__()den folgenden Code einzufügen :

def __init__(self):
    self.reset()
    self.fed = []

... damit es so aussieht:

def __init__(self):
    super().__init__()
    self.reset()
    self.fed = []

... und es wird für Python 3.2.3 funktionieren.

Nochmals vielen Dank an Thomas K für das Update und den oben angegebenen Originalcode von Eloff!


1

Sie können Ihre eigene Funktion schreiben:

def StripTags(text):
     finished = 0
     while not finished:
         finished = 1
         start = text.find("<")
         if start >= 0:
             stop = text[start:].find(">")
             if stop >= 0:
                 text = text[:start] + text[start+stop+1:]
                 finished = 0
     return text

1
Erstellt das Anhängen an Zeichenfolgen eine neue Kopie der Zeichenfolge?
Jeremy L

1
@Nerdling - Ja, was zu beeindruckenden Ineffizienzen bei häufig verwendeten Funktionen führen kann (oder bei selten verwendeten Funktionen, die auf große Textblobs wirken). Weitere Informationen finden Sie auf dieser Seite. : D
Jeremy Sandell

Testet es gegen Anführungszeichen? Nein
Jimmy Kane

1

Die Lösungen mit HTML-Parser sind alle zerbrechlich, wenn sie nur einmal ausgeführt werden:

html_to_text('<<b>script>alert("hacked")<</b>/script>

Ergebnisse in:

<script>alert("hacked")</script>

was Sie verhindern wollen. Wenn Sie einen HTML-Parser verwenden, zählen Sie die Tags, bis Null ersetzt wird:

from HTMLParser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
        self.containstags = False

    def handle_starttag(self, tag, attrs):
       self.containstags = True

    def handle_data(self, d):
        self.fed.append(d)

    def has_tags(self):
        return self.containstags

    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    must_filtered = True
    while ( must_filtered ):
        s = MLStripper()
        s.feed(html)
        html = s.get_data()
        must_filtered = s.has_tags()
    return html

1
Wenn Sie eine aufgerufene Funktion aufrufen html_to_textund den von dieser Funktion ausgegebenen Text in HTML einbetten, ohne diesen Text zu maskieren, ist das fehlende Escaping eine Sicherheitslücke, nicht die html_to_textFunktion. Die html_to_textFunktion hat Ihnen nie versprochen, dass die Ausgabe Text sein würde. Das Einfügen von Text in HTML ohne Escapezeichen ist eine potenzielle Sicherheitslücke, unabhängig davon, ob Sie den Text von html_to_text einer anderen Quelle erhalten haben oder nicht.
Kasperd

Sie haben Recht, wenn Sie nicht entkommen können, aber die Frage war, HTML von einer bestimmten Zeichenfolge zu entfernen, um einer bestimmten Zeichenfolge nicht zu entkommen. Wenn die früheren Antworten aufgrund des Entfernens von HTML neue HTML-Dateien mit ihren Lösungen erstellen, ist die Verwendung dieser Lösungen gefährlich.
Falk Nisius

1

Dies ist eine schnelle Lösung und kann noch optimiert werden, funktioniert aber einwandfrei. Dieser Code ersetzt alle nicht leeren Tags durch "" und entfernt alle HTML-Tags aus einem bestimmten Eingabetext. Sie können ihn mit der Eingabeausgabe ./file.py ausführen

    #!/usr/bin/python
import sys

def replace(strng,replaceText):
    rpl = 0
    while rpl > -1:
        rpl = strng.find(replaceText)
        if rpl != -1:
            strng = strng[0:rpl] + strng[rpl + len(replaceText):]
    return strng


lessThanPos = -1
count = 0
listOf = []

try:
    #write File
    writeto = open(sys.argv[2],'w')

    #read file and store it in list
    f = open(sys.argv[1],'r')
    for readLine in f.readlines():
        listOf.append(readLine)         
    f.close()

    #remove all tags  
    for line in listOf:
        count = 0;  
        lessThanPos = -1  
        lineTemp =  line

            for char in lineTemp:

            if char == "<":
                lessThanPos = count
            if char == ">":
                if lessThanPos > -1:
                    if line[lessThanPos:count + 1] != '<>':
                        lineTemp = replace(lineTemp,line[lessThanPos:count + 1])
                        lessThanPos = -1
            count = count + 1
        lineTemp = lineTemp.replace("&lt","<")
        lineTemp = lineTemp.replace("&gt",">")                  
        writeto.write(lineTemp)  
    writeto.close() 
    print "Write To --- >" , sys.argv[2]
except:
    print "Help: invalid arguments or exception"
    print "Usage : ",sys.argv[0]," inputfile outputfile"

1

Eine Python 3-Adaption der Antwort von søren-løvborg

from html.parser import HTMLParser
from html.entities import html5

class HTMLTextExtractor(HTMLParser):
    """ Adaption of http://stackoverflow.com/a/7778368/196732 """
    def __init__(self):
        super().__init__()
        self.result = []

    def handle_data(self, d):
        self.result.append(d)

    def handle_charref(self, number):
        codepoint = int(number[1:], 16) if number[0] in (u'x', u'X') else int(number)
        self.result.append(unichr(codepoint))

    def handle_entityref(self, name):
        if name in html5:
            self.result.append(unichr(html5[name]))

    def get_text(self):
        return u''.join(self.result)

def html_to_text(html):
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()

1

Für ein Projekt brauchte ich also HTML-Strip, aber auch CSS und JS. Daher habe ich eine Variation von Eloffs Antwort gemacht:

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.strict = False
        self.convert_charrefs= True
        self.fed = []
        self.css = False
    def handle_starttag(self, tag, attrs):
        if tag == "style" or tag=="script":
            self.css = True
    def handle_endtag(self, tag):
        if tag=="style" or tag=="script":
            self.css=False
    def handle_data(self, d):
        if not self.css:
            self.fed.append(d)
    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()

1

Hier ist eine Lösung ähnlich der derzeit akzeptierten Antwort ( https://stackoverflow.com/a/925630/95989 ), außer dass die interne HTMLParserKlasse direkt verwendet wird (dh keine Unterklasse), wodurch sie erheblich knapper wird:

def strip_html (Text):
    Teile = []                                                                      
    parser = HTMLParser ()                                                           
    parser.handle_data = parts.append                                               
    parser.feed (Text)                                                               
    return '' .join (Teile)

0

Ich analysiere Github Readmes und finde, dass Folgendes wirklich gut funktioniert:

import re
import lxml.html

def strip_markdown(x):
    links_sub = re.sub(r'\[(.+)\]\([^\)]+\)', r'\1', x)
    bold_sub = re.sub(r'\*\*([^*]+)\*\*', r'\1', links_sub)
    emph_sub = re.sub(r'\*([^*]+)\*', r'\1', bold_sub)
    return emph_sub

def strip_html(x):
    return lxml.html.fromstring(x).text_content() if x else ''

Und dann

readme = """<img src="https://raw.githubusercontent.com/kootenpv/sky/master/resources/skylogo.png" />

            sky is a web scraping framework, implemented with the latest python versions in mind (3.4+). 
            It uses the asynchronous `asyncio` framework, as well as many popular modules 
            and extensions.

            Most importantly, it aims for **next generation** web crawling where machine intelligence 
            is used to speed up the development/maintainance/reliability of crawling.

            It mainly does this by considering the user to be interested in content 
            from *domains*, not just a collection of *single pages*
            ([templating approach](#templating-approach))."""

strip_markdown(strip_html(readme))

Entfernt alle Markdowns und HTML-Dateien korrekt.


0

Wenn Sie BeautifulSoup, html2text oder den Code von @Eloff verwenden, bleiben meistens einige HTML-Elemente, Javascript-Code ...

Sie können also eine Kombination dieser Bibliotheken verwenden und die Markdown-Formatierung löschen (Python 3):

import re
import html2text
from bs4 import BeautifulSoup
def html2Text(html):
    def removeMarkdown(text):
        for current in ["^[ #*]{2,30}", "^[ ]{0,30}\d\\\.", "^[ ]{0,30}\d\."]:
            markdown = re.compile(current, flags=re.MULTILINE)
            text = markdown.sub(" ", text)
        return text
    def removeAngular(text):
        angular = re.compile("[{][|].{2,40}[|][}]|[{][*].{2,40}[*][}]|[{][{].{2,40}[}][}]|\[\[.{2,40}\]\]")
        text = angular.sub(" ", text)
        return text
    h = html2text.HTML2Text()
    h.images_to_alt = True
    h.ignore_links = True
    h.ignore_emphasis = False
    h.skip_internal_links = True
    text = h.handle(html)
    soup = BeautifulSoup(text, "html.parser")
    text = soup.text
    text = removeAngular(text)
    text = removeMarkdown(text)
    return text

Es funktioniert gut für mich, aber es kann natürlich verbessert werden ...


0

Einfacher Code!. Dadurch werden alle darin enthaltenen Tags und Inhalte entfernt.

def rm(s):
    start=False
    end=False
    s=' '+s
    for i in range(len(s)-1):
        if i<len(s):
            if start!=False:
                if s[i]=='>':
                    end=i
                    s=s[:start]+s[end+1:]
                    start=end=False
            else:
                if s[i]=='<':
                    start=i
    if s.count('<')>0:
        self.rm(s)
    else:
        s=s.replace('&nbsp;', ' ')
        return s

Es wird jedoch kein vollständiges Ergebnis erzielt, wenn der Text <> Symbole enthält .


0
# This is a regex solution.
import re
def removeHtml(html):
  if not html: return html
  # Remove comments first
  innerText = re.compile('<!--[\s\S]*?-->').sub('',html)
  while innerText.find('>')>=0: # Loop through nested Tags
    text = re.compile('<[^<>]+?>').sub('',innerText)
    if text == innerText:
      break
    innerText = text

  return innerText.strip()

-2

Diese Methode funktioniert bei mir einwandfrei und erfordert keine zusätzlichen Installationen:

import re
import htmlentitydefs

def convertentity(m):
    if m.group(1)=='#':
        try:
            return unichr(int(m.group(2)))
        except ValueError:
            return '&#%s;' % m.group(2)
        try:
            return htmlentitydefs.entitydefs[m.group(2)]
        except KeyError:
            return '&%s;' % m.group(2)

def converthtml(s):
    return re.sub(r'&(#?)(.+?);',convertentity,s)

html =  converthtml(html)
html.replace("&nbsp;", " ") ## Get rid of the remnants of certain formatting(subscript,superscript,etc).

3
Dadurch werden HTML-Entitäten in einfachen Text dekodiert, aber offensichtlich werden keine Tags entfernt, was die ursprüngliche Frage war. (Außerdem muss der zweite Try-Except-Block deaktiviert werden, damit der Code überhaupt so viel kann.)
Søren Løvborg
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.