Rimuovi HTML dalle stringhe in Python


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

Quando stampo una riga in un file HTML, sto cercando di trovare un modo per mostrare solo il contenuto di ciascun elemento HTML e non la formattazione stessa. Se lo trova '<a href="whatever.com">some text</a>', stampa solo 'un po' di testo ', '<b>hello</b>'stampa' ciao ', ecc. Come si potrebbe fare?


16
Una considerazione importante è come gestire entità HTML (ad es &amp;.). È possibile 1) rimuoverli insieme ai tag (spesso indesiderabili e non necessari in quanto equivalenti al testo normale), 2) lasciarli invariati (una soluzione adatta se il testo rimosso viene ripristinato direttamente in un contesto HTML) o 3 ) decodificali in testo semplice (se il testo rimosso viene inserito in un database o in qualche altro contesto non HTML o se il tuo framework web esegue automaticamente l'escaping HTML del testo per te).
Søren Løvborg,

2
per @ SørenLøvborg punto 2): stackoverflow.com/questions/753052/…
Robert

2
La risposta principale qui, che è stata utilizzata dal progetto Django fino a marzo 2014, è risultata insicura contro lo scripting cross-site - vedi quel link per un esempio che lo supera. Consiglio di usare Bleach.clean (), gli striptag di Markupsafe o gli strip_tags di RECENT Django.
rescdsk,

Risposte:


419

Ho sempre usato questa funzione per rimuovere i tag HTML, poiché richiede solo lo stdlib di Python:

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

Per 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
Due anni o più, affrontando lo stesso problema, e questa è una soluzione molto più elegante. L'unica modifica che ho apportato è stata quella di restituire self.fed come elenco, piuttosto che unirmi a esso, in modo da poter scorrere il contenuto dell'elemento.
direzione

47
Si noti che questo elimina le entità HTML (ad esempio &amp;) e i tag.
Søren Løvborg,

30
@surya Sono sicuro che hai visto questo
fino al

8
Grazie per la magnifica risposta. Una cosa da notare per quelli di voi che usano le versioni più recenti di Python (3.2+) è che dovrete chiamare la __init__funzione della classe genitore . Vedi qui: stackoverflow.com/questions/11061058/… .
pseudoramble,

10
Per mantenere le entità html (convertite in unicode), ho aggiunto due righe: parser = HTMLParser()e html = parser.unescape(html)all'inizio della funzione strip_tags.
James Doepp - pihentagyu,

157

Non ho pensato molto ai casi che ci mancheranno, ma puoi fare una semplice regex:

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

Per coloro che non capiscono regex, questo cerca una stringa <...>, in cui il contenuto interno è composto da uno o più ( +) caratteri che non sono a <. Ciò ?significa che corrisponderà alla stringa più piccola che riesce a trovare. Ad esempio <p>Hello</p>, corrisponderà <'p>e </p>separatamente con ?. Senza di essa, corrisponderà all'intera stringa <..Hello..>.

Se non-tag <appare in HTML (ad es. 2 < 3), Dovrebbe essere scritto comunque come una sequenza di escape, &...quindi ^<potrebbe non essere necessario.


10
Questo è quasi esattamente il modo in cui lo strip_tags di Django lo fa.
Bluu,

10
Si noti che questo lascia entità HTML (ad es. &amp;) Invariate nell'output.
Søren Løvborg,

36
Si può ancora ingannare questo metodo con qualcosa del genere: <script <script>> alert ("Ciao!") <</script> / script>

19
NON FARLO IN QUESTO MODO! Come dice @Julio Garcia, NON È SICURO!
rescdsk,

18
Persone, non confondere lo stripping HTML e la sanificazione HTML. Sì, per input errati o dannosi questa risposta può produrre output con tag HTML al suo interno. È ancora un approccio perfettamente valido per eliminare i tag HTML. Tuttavia , rimuovere i tag HTML non è una sostituzione valida per una corretta sanificazione HTML. La regola non è difficile: ogni volta che inserisci una stringa di testo semplice nell'output HTML, dovresti sempre scappare da HTML (usando cgi.escape(s, True)), anche se "sai" che non contiene HTML (ad esempio perché hai rimosso il contenuto HTML) . Tuttavia, questo non è ciò che l'OP ha chiesto.
Søren Løvborg,

77

Puoi usare la get_text()funzione BeautifulSoup .

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)

È consigliabile specificare esplicitamente il parser , ad esempio come BeautifulSoup(html_str, features="html.parser"), affinché l'output sia riproducibile.


32

Versione breve!

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)

Fonte Regex: MarkupSafe . La loro versione gestisce anche entità HTML, mentre questa veloce no.

Perché non posso semplicemente rimuovere i tag e lasciarlo?

Una cosa è proteggere le persone dalle <i>italicizing</i>cose, senza lasciarle ifluttuare. Ma è un altro prendere input arbitrari e renderlo completamente innocuo. La maggior parte delle tecniche in questa pagina lascerà intatte cose come commenti non chiusi ( <!--) e parentesi angolari che non fanno parte dei tag ( blah <<<><blah). La versione HTMLParser può anche lasciare tag completi, se si trovano all'interno di un commento non chiuso.

E se il tuo modello fosse {{ firstname }} {{ lastname }}? firstname = '<a'e lastname = 'href="http://evil.com/">'saranno lasciati passare da ogni tag stripper su questa pagina (tranne @Medeiros!), perché non sono tag completi da soli. Eliminare i normali tag HTML non è sufficiente.

Django strip_tags, una versione migliorata (vedi intestazione successiva) della risposta principale a questa domanda, dà il seguente avvertimento:

Assolutamente NESSUNA garanzia è fornita sul fatto che la stringa risultante sia HTML sicura. Quindi NON contrassegnare MAI in sicurezza il risultato di una strip_tagschiamata senza prima evitarla, ad esempio con escape().

Segui i loro consigli!

Per rimuovere i tag con HTMLParser, devi eseguirlo più volte.

È facile aggirare la risposta migliore a questa domanda.

Guarda questa stringa ( fonte e discussione ):

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

La prima volta che HTMLParser lo vede, non può dire che <img...>è un tag. Sembra rotto, quindi HTMLParser non si sbarazza di esso. Elimina solo il <!-- comments -->, lasciandoti con

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

Questo problema è stato rivelato al progetto Django nel marzo 2014. Il loro vecchio strip_tagsera essenzialmente lo stesso della risposta migliore a questa domanda. La loro nuova versione praticamente lo esegue in un ciclo fino a quando non viene eseguito nuovamente non cambia la stringa:

# _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

Naturalmente, nulla di tutto questo è un problema se si sfugge sempre al risultato di strip_tags().

Aggiornamento del 19 marzo 2015 : c'era un bug nelle versioni di Django precedenti alla 1.4.20, 1.6.11, 1.7.7 e 1.8c1. Queste versioni potrebbero inserire un ciclo infinito nella funzione strip_tags (). La versione fissa è riprodotta sopra. Maggiori dettagli qui .

Buone cose da copiare o usare

Il mio codice di esempio non gestisce le entità HTML, come invece fanno le versioni in pacchetto Django e MarkupSafe.

Il mio codice di esempio viene estratto dall'eccellente libreria MarkupSafe per la prevenzione degli script tra siti. È comodo e veloce (con accelerazioni C nella versione nativa di Python). È incluso in Google App Engine e utilizzato da Jinja2 (2.7 e versioni successive) , Mako, Pylons e altro. Funziona facilmente con i modelli Django di Django 1.7.

Gli strip_tags di Django e altre utility html di una versione recente sono buoni, ma li trovo meno convenienti di MarkupSafe. Sono piuttosto indipendenti, puoi copiare quello che ti serve da questo file .

Se devi rimuovere quasi tutti i tag, la libreria Bleach è buona. Puoi far applicare regole come "i miei utenti possono scrivere in corsivo le cose, ma non possono creare iframe".

Comprendi le proprietà del tuo tag stripper! Esegui test fuzz su di esso! Ecco il codice che ho usato per fare la ricerca per questa risposta.

nota imbarazzata - La domanda in sé riguarda la stampa sulla console, ma questo è il miglior risultato di Google per "html da stringa in pitone da stringa", quindi questa risposta è del 99% sul web.


Il mio codice di esempio "ultima riga alternativa" non gestisce le entità html - quanto è grave?
rescdsk,

Sto solo analizzando un piccolo pezzo di HTML senza tag speciali, e la tua versione corta fa molto bene il lavoro. Grazie per la condivisione!
Tbolender,

31

Avevo bisogno di un modo per eliminare tag e decodificare entità HTML in testo semplice. La seguente soluzione si basa sulla risposta di Eloff (che non ho potuto usare perché spoglia le entità).

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

Un rapido test:

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

Risultato:

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

Gestione degli errori:

  • Una struttura HTML non valida può causare un HTMLParseError .
  • Le entità HTML con nome non valide (come &#apos;, che sono valide in XML e XHTML, ma non in semplice HTML) causeranno ValueErrorun'eccezione.
  • Le entità HTML numeriche che specificano punti di codice al di fuori dell'intervallo Unicode accettabili da Python (come, su alcuni sistemi, i caratteri al di fuori del Piano multilingue di base ) causeranno ValueErrorun'eccezione.

Nota di sicurezza: non confondere l'eliminazione dell'HTML (conversione dell'HTML in testo semplice) con la disinfezione dell'HTML (conversione del testo semplice in HTML). Questa risposta rimuoverà HTML e decodificherà le entità in testo semplice, il che non rende il risultato sicuro da usare in un contesto HTML.

Esempio: &lt;script&gt;alert("Hello");&lt;/script&gt;verrà convertito in <script>alert("Hello");</script>, che è un comportamento corretto al 100%, ma ovviamente non sufficiente se il testo in chiaro risultante viene inserito così com'è in una pagina HTML.

La regola non è difficile: ogni volta che inserisci una stringa di testo semplice nell'output HTML, dovresti sempre scappare da HTML (usando cgi.escape(s, True)), anche se "sai" che non contiene HTML (ad esempio perché hai rimosso il contenuto HTML) .

(Tuttavia, l'OP ha chiesto di stampare il risultato sulla console, nel qual caso non è necessaria la fuga HTML.)

Python versione 3.4+: (con 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()

Si noti che HTMLParser è migliorato in Python 3 (ovvero meno codice e migliore gestione degli errori).


18

C'è un modo semplice per questo:

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

L'idea è spiegata qui: http://youtu.be/2tu9LTDujbw

Puoi vederlo funzionare qui: http://youtu.be/HPkNPcYed9M?t=35s

PS: se sei interessato alla classe (sul debug intelligente con Python) ti do un link: http://www.udacity.com/overview/Course/cs259/CourseRev/1 . È gratis!

Prego! :)


2
Mi chiedo perché questa risposta sia stata appena votata. È un modo semplice per risolvere il problema senza alcuna lib. Solo puro pitone e funziona come mostrato dai link.
Medeiros,

2
Probabilmente le persone preferiscono le librerie per dare loro sicurezza. Ho testato il tuo codice e ho passato, e preferisco sempre un piccolo codice che comprendo rispetto all'uso di una lib e supponendo che sia ok fino a quando non viene visualizzato un bug. Per me è quello che stavo cercando e grazie ancora. Per quanto riguarda i voti negativi, non entrare in quella mentalità. Le persone qui dovrebbero preoccuparsi della qualità e non dei voti. Ultimamente SO è diventato un luogo dove tutti vogliono i punti e non la conoscenza.
Jimmy Kane,

2
Il problema con questa soluzione è la gestione degli errori. Ad esempio se si danno <b class="o'>x</b>come funzioni di input output x. Ma in realtà questo input non è valido. Penso che sia per questo che la gente preferisce le librerie.
Laltina,

1
Funziona anche con quell'input. Appena testato. Basta rendersi conto che all'interno di quelle librerie troverai codice simile. Non è molto pitonico, lo so. Sembra codice C o Java. Penso che sia efficiente e possa essere facilmente trasferito in un'altra lingua.
Medeiros,

1
Semplice, Pythonic e sembra funzionare altrettanto bene o meglio di tutti gli altri metodi discussi. È possibile che non funzionerà per alcuni HTML mal formati ma non è possibile superarli.
denson,

16

Se hai bisogno di preservare entità HTML (es. &amp;), Ho aggiunto il metodo "handle_entityref" alla risposta di Eloff .

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

Se vuoi rimuovere tutti i tag HTML, il modo più semplice che ho trovato è utilizzare BeautifulSoup:

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

Ho provato il codice della risposta accettata ma stavo ottenendo "RuntimeError: superata la profondità massima di ricorsione", che non si è verificato con il blocco di codice sopra riportato.


1
Ho appena provato il tuo metodo perché sembra più pulito, ha funzionato, in un certo senso ... non ha rimosso i tag di input!
kustomrtr,

Trovo che una semplice applicazione di BeautifulSoup ha un problema con spaziature: ''.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True)). Qui l'output è "helloworld", mentre probabilmente si desidera che sia "ciao mondo". ' '.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True))non aiuta in quanto diventa "he llo world".
Finn Årup Nielsen,

@kustomrtr, scusate la mia ignoranza, cosa metto nell'argomento personale? NameError: il nome 'self' non è definito
Ian_De_Oliveira

@Ian_De_Oliveira Puoi rimuoverlo, ho pensato che fosse all'interno di una classe ma non è necessario. Ho anche modificato la risposta per rimuoverla
Vasilis,

@Ian_De_Oliveira Puoi rimuoverlo, ho pensato che fosse all'interno di una classe ma non è necessario. Ho anche modificato la risposta per rimuoverla
Vasilis,

10

Ecco una semplice soluzione che elimina i tag HTML e decodifica le entità HTML in base alla lxmllibreria incredibilmente veloce :

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
A partire dal 2020, questo era il modo più veloce e migliore per eliminare i contenuti dell'HTML. Più il vantaggio di gestire la decodifica. Ottimo per il rilevamento della lingua!
dfabiano,

text_content()ritorna lxml.etree._ElementUnicodeResultquindi potresti
doverlo

1
@Suzana Un buon punto. Sembra essere auto-castato strper operazioni su stringa come +e indicizzazione []. Aggiunto comunque un cast per una buona misura.
Robin Dinse,

9

Una soluzione basata su lxml.html (lxml è una libreria nativa e quindi molto più veloce di qualsiasi soluzione Python pura).

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

Vedi anche http://lxml.de/lxmlhtml.html#cleaning-up-html per quello che fa esattamente lxml.cleaner.

Se hai bisogno di un maggiore controllo su cosa è esattamente sterilizzato prima di convertirlo in testo, allora potresti voler usare esplicitamente lxml Cleaner passando le opzioni che desideri nel costruttore, ad esempio:

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
Ho ottenuto AttributeError: l'oggetto 'HtmlElement' non ha attributo 'strip'
aris

7

Il pacchetto Beautiful Soup lo fa immediatamente per te.

from bs4 import BeautifulSoup

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

3
Dalla coda delle recensioni: posso chiederti di aggiungere un altro contesto alla tua risposta. Le risposte di solo codice sono difficili da capire. Aiuterà il richiedente e i futuri lettori sia se è possibile aggiungere ulteriori informazioni nel tuo post.
help-info.de

2

Ecco la mia soluzione per 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

Non sono sicuro che sia perfetto, ma ho risolto il mio caso d'uso e sembra semplice.


2

Puoi usare un altro parser HTML ( come lxml o Beautiful Soup ), che offre funzioni per estrarre solo testo. In alternativa, puoi eseguire una regex sulla stringa di riga che rimuove i tag. Consulta i documenti Python per ulteriori informazioni.


1
il link amk è morto. Hai un'alternativa?

2
Il sito Web di Python ora ha buone istruzioni, ecco il regex how-to: docs.python.org/howto/regex
Jason Coon

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

1
L'esempio di Bluu con lxml decodifica entità HTML (ad esempio &amp;) in testo.
Søren Løvborg,

1

Ho usato la risposta di Eloff con successo per Python 3.1 [molte grazie!].

Ho eseguito l'aggiornamento a Python 3.2.3 e ho riscontrato errori.

La soluzione, fornita qui grazie al risponditore Thomas K, è quella di inserire super().__init__()nel seguente codice:

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

... per farlo apparire così:

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

... e funzionerà con Python 3.2.3.

Ancora una volta, grazie a Thomas K per la correzione e per il codice originale di Eloff fornito sopra!


1

Puoi scrivere la tua funzione:

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
L'aggiunta alle stringhe crea una nuova copia della stringa?
Jeremy L

1
@Nerdling - Sì, il che può portare ad alcune inefficienze piuttosto impressionanti nelle funzioni usate di frequente (o, del resto, funzioni usate di rado che agiscono su grandi blocchi di testo). Vedi questa pagina per i dettagli. : D
Jeremy Sandell,

Verifica le stringhe tra virgolette? No.
Jimmy Kane

1

Le soluzioni con HTML-Parser sono tutte fragili, se funzionano solo una volta:

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

risulta in:

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

cosa intendi prevenire. se usi un parser HTML, conta i tag fino a quando non vengono sostituiti zero:

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
Se chiami una funzione chiamata html_to_texte incorpori il testo in uscita da quella funzione in HTML senza sfuggire a quel testo, allora è la mancanza di fuga, che è una vulnerabilità di sicurezza, non la html_to_textfunzione. La html_to_textfunzione non ti ha mai promesso che l'output sarebbe testo. E l'inserimento di testo in HTML senza escape è una potenziale vulnerabilità di sicurezza indipendentemente dal fatto che tu abbia ricevuto il testo html_to_text o qualche altra fonte.
Kasperd,

Hai ragione nel caso di una mancanza di escape, ma la domanda era di rimuovere l'html da una determinata stringa per non sfuggire a una determinata stringa. Se le risposte precedenti creano un nuovo codice HTML con le loro soluzioni a seguito della rimozione di un codice HTML, l'uso di queste soluzioni è pericoloso.
Falk Nisius,

1

Questa è una soluzione rapida e può essere ancora più ottimizzata ma funzionerà benissimo. Questo codice sostituirà tutti i tag non vuoti con "" e rimuoverà tutti i tag HTML da un dato testo di input. Puoi eseguirlo usando l'output di input ./file.py

    #!/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

Un adattamento di Python 3 della risposta di 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

Per un progetto, avevo bisogno di spogliare HTML, ma anche css e js. Quindi, ho fatto una variazione della risposta di Eloffs:

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

Ecco una soluzione simile alla risposta attualmente accettata ( https://stackoverflow.com/a/925630/95989 ), tranne per il fatto che utilizza HTMLParserdirettamente la classe interna (cioè nessuna sottoclasse), rendendola significativamente più concisa:

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

0

Sto analizzando i readme di Github e trovo che quanto segue funzioni davvero bene:

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 ''

E poi

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

Rimuove correttamente tutti i markdown e html.


0

Usando BeautifulSoup, html2text o il codice di @Eloff, il più delle volte, rimangono alcuni elementi html, codice javascript ...

Quindi puoi usare una combinazione di queste librerie ed eliminare la formattazione del markdown (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

Funziona bene per me ma può essere migliorato, ovviamente ...


0

Codice semplice !. Ciò rimuoverà tutti i tipi di tag e contenuti al suo interno.

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

Ma non darà pieno risultato se il testo contiene <> simboli al suo interno.


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

Questo metodo funziona perfettamente per me e non richiede installazioni aggiuntive:

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
Questo decodifica le entità HTML in testo semplice, ma ovviamente non toglie alcun tag, che era la domanda originale. (Inoltre, il secondo blocco try-tranne deve essere rientrato per consentire al codice di fare altrettanto).
Søren Løvborg,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.