Eliminar HTML de cadenas en Python

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

Cuando imprimo una línea en un archivo HTML, intento encontrar una manera de mostrar solo el contenido de cada elemento HTML y no el formato en sí. Si encuentra '<a href="whatever.com">some text</a>', solo imprimirá 'algo de texto', '<b>hello</b>'imprimirá 'hola', etc. ¿Cómo se podría hacer esto?

direccion
fuente
16
Una consideración importante es cómo manejar entidades HTML (por ejemplo &amp;). Puede 1) eliminarlos junto con las etiquetas (a menudo indeseables e innecesarios, ya que son equivalentes a texto sin formato), 2) dejarlos sin cambios (una solución adecuada si el texto eliminado vuelve directamente a un contexto HTML) o 3 ) los decodifica en texto sin formato (si el texto eliminado se va a una base de datos o algún otro contexto que no sea HTML, o si su marco web realiza automáticamente el escape de texto HTML por usted).
Søren Løvborg
2
para @ SørenLøvborg punto 2): stackoverflow.com/questions/753052/…
Robert
2
La respuesta más común aquí, que fue utilizado por el proyecto Django hasta marzo de 2014, se ha encontrado que es inseguro frente a cross-site scripting - ver ese enlace para ver un ejemplo que lo hace a través. Recomiendo usar Bleach.clean (), los striptags de Markupsafe o los strip_tags RECIENTES de Django.
Rescdsk

Respuestas:

419

Siempre usé esta función para eliminar las etiquetas HTML, ya que solo requiere Python stdlib:

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

Para 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()
Olivier Le Floch
fuente
3
Dos años más tarde, enfrenta el mismo problema, y ​​esta es una solución mucho más elegante. El único cambio que hice fue devolver self.fed como una lista, en lugar de unirla, para poder revisar el contenido del elemento.
dirección
47
Tenga en cuenta que esto elimina las entidades HTML (por ejemplo &amp;), así como las etiquetas.
Søren Løvborg
30
@surya Estoy seguro de que has visto esto
tkone
8
Gracias por la gran respuesta. Una cosa a tener en cuenta para aquellos de ustedes que usan versiones más recientes de Python (3.2+) es que deberán llamar a la __init__función de la clase principal . Ver aquí: stackoverflow.com/questions/11061058/… .
pseudoramble
10
Para mantener las entidades html (convertidas a unicode), agregué dos líneas: parser = HTMLParser()y html = parser.unescape(html)al comienzo de la función strip_tags.
James Doepp - pihentagyu
157

No he pensado mucho en los casos que se perderán, pero puedes hacer una expresión regular simple:

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

Para aquellos que no entienden la expresión regular, esto busca una cadena <...>, donde el contenido interno está formado por uno o más ( +) caracteres que no es un <. Esto ?significa que coincidirá con la cadena más pequeña que pueda encontrar. Por ejemplo <p>Hello</p>, coincidirá <'p>y por </p>separado con el ?. Sin ella, coincidirá con toda la cadena <..Hello..>.

Si no <aparece etiqueta en html (p. Ej. 2 < 3), Debe escribirse como una secuencia de escape de &...todos modos, por lo que ^<puede ser innecesario.

mmmdreg
fuente
10
Esto es casi exactamente como lo hace strip_tags de Django .
Bluu
10
Tenga en cuenta que esto deja las entidades HTML (por ejemplo &amp;) sin cambios en la salida.
Søren Løvborg
36
Todavía se puede engañar a este método con algo como esto: <script <script>> alert ("Hi!") <</script> / script>
19
¡NO LO HAGAS DE ESTA MANERA! Como dice @Julio García, ¡NO ES SEGURO!
rescdsk
18
Gente, no confunda la eliminación de HTML y la desinfección de HTML. Sí, para entradas dañadas o dañadas, esta respuesta puede producir resultados con etiquetas HTML. Sigue siendo un enfoque perfectamente válido para eliminar las etiquetas HTML. Sin embargo , eliminar las etiquetas HTML no es una sustitución válida para la correcta desinfección HTML. La regla no es difícil: cada vez que inserte una cadena de texto sin formato en la salida HTML, siempre debe escapar de HTML (usando cgi.escape(s, True)), incluso si "sabe" que no contiene HTML (por ejemplo, porque eliminó el contenido HTML) . Sin embargo, esto no es lo que OP preguntó.
Søren Løvborg
77

Puede usar la get_text()función 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)

Es recomendable especificar explícitamente el analizador , por ejemplo BeautifulSoup(html_str, features="html.parser"), como , para que la salida sea reproducible.

Aminah Nuraini
fuente
32

¡Version corta!

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)

Fuente de expresiones regulares: MarkupSafe . Su versión también maneja entidades HTML, mientras que esta rápida no lo hace.

¿Por qué no puedo simplemente quitar las etiquetas y dejarlo?

Una cosa es mantener a las personas alejadas de las <i>italicizing</i>cosas, sin dejar is flotando. Pero otra es tomar aportaciones arbitrarias y hacerlo completamente inofensivo. La mayoría de las técnicas en esta página dejarán intactos elementos como los comentarios no cerrados ( <!--) y los corchetes angulares que no forman parte de las etiquetas ( blah <<<><blah). La versión HTMLParser puede incluso dejar etiquetas completas, si están dentro de un comentario no cerrado.

¿Qué pasa si su plantilla es {{ firstname }} {{ lastname }}? firstname = '<a'y lastname = 'href="http://evil.com/">'todos los separadores de etiquetas de esta página lo dejarán pasar (¡excepto @Medeiros!), porque no son etiquetas completas por sí mismas. Eliminar las etiquetas HTML normales no es suficiente.

Django's strip_tags, una versión mejorada (ver el siguiente encabezado) de la respuesta principal a esta pregunta, da la siguiente advertencia:

Absolutamente NO se proporciona ninguna garantía acerca de que la cadena resultante sea segura para HTML. Por lo tanto, NUNCA marque con seguridad el resultado de una strip_tagsllamada sin escapar primero, por ejemplo con escape().

¡Sigue sus consejos!

Para eliminar etiquetas con HTMLParser, debe ejecutarlo varias veces.

Es fácil eludir la respuesta principal a esta pregunta.

Mire esta cadena ( fuente y discusión ):

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

La primera vez que HTMLParser lo ve, no puede decir que <img...>es una etiqueta. Parece roto, por lo que HTMLParser no se deshace de él. Solo saca el <!-- comments -->, dejándote con

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

Este problema fue revelado al proyecto Django en marzo de 2014. Su antiguo strip_tagsera esencialmente el mismo que la respuesta principal a esta pregunta. Su nueva versión básicamente lo ejecuta en un bucle hasta que ejecutarlo nuevamente no cambia la cadena:

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

Por supuesto, nada de esto es un problema si siempre escapas del resultado de strip_tags().

Actualización 19 de marzo de 2015 : hubo un error en las versiones de Django anteriores a 1.4.20, 1.6.11, 1.7.7 y 1.8c1. Estas versiones podrían entrar en un bucle infinito en la función strip_tags (). La versión fija se reproduce arriba. Más detalles aquí .

Cosas buenas para copiar o usar

Mi código de ejemplo no maneja entidades HTML, las versiones empaquetadas de Django y MarkupSafe sí.

Mi código de ejemplo se extrae de la excelente biblioteca MarkupSafe para la prevención de secuencias de comandos entre sitios. Es conveniente y rápido (con aceleraciones en C a su versión nativa de Python). Está incluido en Google App Engine y lo utilizan Jinja2 (2.7 y versiones posteriores) , Mako, Pilones y más. Funciona fácilmente con las plantillas de Django de Django 1.7.

Strip_tags de Django y otras utilidades html de una versión reciente son buenas, pero las encuentro menos convenientes que MarkupSafe. Son bastante independientes, puede copiar lo que necesita de este archivo .

Si necesita quitar casi todas las etiquetas, la biblioteca de Bleach es buena. Puede hacer que aplique reglas como "mis usuarios pueden poner en cursiva las cosas, pero no pueden hacer iframes".

¡Comprenda las propiedades de su etiqueta stripper! Ejecute pruebas de fuzz en él! Aquí está el código que usé para hacer la investigación de esta respuesta.

nota tímida : la pregunta en sí misma se trata de imprimir en la consola, pero este es el principal resultado de Google para "python strip html from string", por lo que esta respuesta es 99% sobre la web.

rescdsk
fuente
Mi código de ejemplo de "última línea alternativa" no maneja entidades html. ¿Qué tan malo es eso?
rescdsk
Solo estoy analizando una pequeña porción de html sin etiquetas especiales, y su versión corta hace el trabajo muy bien. ¡Gracias por compartir!
tbolender
31

Necesitaba una forma de quitar etiquetas y decodificar entidades HTML en texto plano. La siguiente solución se basa en la respuesta de Eloff (que no pude usar porque elimina las entidades).

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

Una prueba rápida:

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

Resultado:

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

Manejo de errores:

  • La estructura HTML no válida puede causar un HTMLParseError .
  • Las entidades HTML con nombre no &#apos;válidas (como , que es válido en XML y XHTML, pero no HTML simple) provocarán unValueError excepción.
  • Las entidades HTML numéricas que especifican puntos de código fuera del rango Unicode aceptable para Python (como, en algunos sistemas, caracteres fuera del plano multilingüe básico ) causarán una ValueErrorexcepción.

Nota de seguridad: no confunda la eliminación de HTML (convertir HTML en texto sin formato) con la desinfección de HTML (convertir texto sin formato en HTML). Esta respuesta eliminará HTML y descodificará entidades en texto sin formato, lo que no hace que el resultado sea seguro de usar en un contexto HTML.

Ejemplo: &lt;script&gt;alert("Hello");&lt;/script&gt;se convertirá a <script>alert("Hello");</script>, que es un comportamiento 100% correcto, pero obviamente no es suficiente si el texto plano resultante se inserta como está en una página HTML.

La regla no es difícil: cada vez que inserte una cadena de texto sin formato en la salida HTML, siempre debe escapar de HTML (usando cgi.escape(s, True)), incluso si "sabe" que no contiene HTML (por ejemplo, porque eliminó el contenido HTML) .

(Sin embargo, el OP preguntó acerca de imprimir el resultado en la consola, en cuyo caso no se necesita escapar de HTML).

Versión de Python 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()

Tenga en cuenta que HTMLParser ha mejorado en Python 3 (lo que significa menos código y mejor manejo de errores).

Søren Løvborg
fuente
18

Hay una manera simple de hacer esto:

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

La idea se explica aquí: http://youtu.be/2tu9LTDujbw

Puedes verlo trabajando aquí: http://youtu.be/HPkNPcYed9M?t=35s

PD: si está interesado en la clase (sobre depuración inteligente con python), le doy un enlace: http://www.udacity.com/overview/Course/cs259/CourseRev/1 . ¡Es gratis!

¡De nada! :)

Medeiros
fuente
2
Me pregunto por qué esta respuesta acaba de ser rechazada. Es una manera simple de resolver el problema sin ninguna lib. Solo Python puro y funciona como se muestra en los enlaces.
Medeiros
2
Probablemente las personas prefieren libs para darles seguridad. Probé su código y lo aprobé, y siempre prefiero un código pequeño que entiendo que usar una lib y asumir que está bien hasta que aparezca un error. Para mí eso es lo que estaba buscando y nuevamente gracias. Con respecto a los votos negativos, no entres en esa mentalidad. La gente aquí debería preocuparse por la calidad y no por los votos. Últimamente SO se ha convertido en un lugar donde todos quieren puntos y no conocimiento.
Jimmy Kane el
2
El problema con esta solución es el manejo de errores. Por ejemplo si das <b class="o'>x</b>como función de entrada salidas x. Pero en realidad esta entrada no es válida. Creo que es por eso que las personas prefieren libs.
laltin
1
Funciona con esa entrada también. Acabo de probar. Solo tenga en cuenta que dentro de esas bibliotecas encontrará un código similar. No es muy pitónico, lo sé. Parece un código C o Java. Creo que es eficiente y puede portarse fácilmente a otro idioma.
Medeiros
1
Simple, Pythonic y parece funcionar tan bien o mejor que cualquiera de los otros métodos discutidos. Es posible que no funcione para algunos HTML mal formados, pero no hay forma de superarlo.
denson
16

Si necesita preservar entidades HTML (es decir &amp;), agregué el método "handle_entityref" a la respuesta de 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()
Robert
fuente
13

Si desea quitar todas las etiquetas HTML, la forma más fácil que encontré es usar 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)) 

Probé el código de la respuesta aceptada pero estaba obteniendo "RuntimeError: excedió la profundidad máxima de recursión", lo que no sucedió con el bloque de código anterior.

Vasilis
fuente
1
Acabo de probar su método porque parece más limpio, funcionó, bueno ... ¡no eliminó las etiquetas de entrada!
kustomrtr
Me parece que una sencilla aplicación de BeautifulSoup tiene un problema con espacios en blanco: ''.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True)). Aquí la salida es "helloworld", mientras que probablemente quieras que sea "hello world". ' '.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True))no ayuda ya que se convierte en "he llo world".
Finn Årup Nielsen
@kustomrtr, disculpa mi ignorancia, ¿qué pongo en el argumento personal? NameError: el nombre 'self' no está definido
Ian_De_Oliveira
@Ian_De_Oliveira Puede eliminarlo, supuse que está dentro de una clase pero no es necesario. También edité la respuesta para eliminarla
Vasilis
@Ian_De_Oliveira Puede eliminarlo, supuse que está dentro de una clase pero no es necesario. También edité la respuesta para eliminarla
Vasilis
10

Aquí hay una solución simple que elimina las etiquetas HTML y decodifica entidades HTML basadas en la lxmlbiblioteca increíblemente rápida :

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.
Robin Dinse
fuente
3
A partir de 2020, esta era la mejor y más rápida forma de eliminar los contenidos del HTML. Además de la ventaja de manejar la decodificación. ¡Excelente para la detección de idiomas!
dfabiano
text_content()regresa, lxml.etree._ElementUnicodeResultasí que quizás tengas que lanzarlo primero a la cuerda
Suzana
1
@Suzana Buen punto. Parece que se convierte automáticamente stren operaciones de cadena como +indexación []. Se agregó un elenco de buena medida de todos modos.
Robin Dinse
9

Una solución basada en lxml.html (lxml es una biblioteca nativa y, por lo tanto, mucho más rápida que cualquier solución de 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

Consulte también http://lxml.de/lxmlhtml.html#cleaning-up-html para saber qué hace exactamente lxml.cleaner.

Si necesita más control sobre lo que se desinfecta exactamente antes de convertirlo a texto, puede usar explícitamente el Limpiador lxml pasando las opciones que desee en el constructor, por ejemplo:

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)
ccpizza
fuente
1
Obtuve AttributeError: el objeto 'HtmlElement' no tiene atributo 'strip'
aris
7

El paquete Beautiful Soup hace esto de inmediato por usted.

from bs4 import BeautifulSoup

soup = BeautifulSoup(html)
text = soup.get_text()
print(text)
fugitivo
fuente
3
De la cola de revisión: ¿ Puedo solicitarle que agregue más contexto alrededor de su respuesta? Las respuestas de solo código son difíciles de entender. Ayudará tanto al autor de la pregunta como a los futuros lectores si puede agregar más información en su publicación.
help-info.de
2

Aquí está mi solución para 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

No estoy seguro de si es perfecto, pero resolvió mi caso de uso y parece simple.

John Loutzenhiser
fuente
2

Puede usar un analizador HTML diferente ( como lxml o Beautiful Soup ), uno que ofrece funciones para extraer solo texto. O bien, puede ejecutar una expresión regular en su cadena de línea que elimina las etiquetas. Vea los documentos de Python para más información.

Jason Coon
fuente
1
Amk Link está muerto. ¿Tienes una alternativa?
2
El sitio web de Python tiene buenos procedimientos ahora, aquí está el tutorial
Jason Coon el
55
En lxml:lxml.html.fromstring(s).text_content()
Bluu
1
El ejemplo de Bluu con lxml decodifica entidades HTML (p &amp;. Ej. ) En texto.
Søren Løvborg
1

He usado la respuesta de Eloff con éxito para Python 3.1 [¡muchas gracias!].

Actualicé a Python 3.2.3 y encontré errores.

La solución, proporcionada aquí gracias al respondedor Thomas K, es insertar super().__init__()en el siguiente código:

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

... para que se vea así:

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

... y funcionará para Python 3.2.3.

Nuevamente, ¡gracias a Thomas K por la solución y por el código original de Eloff provisto arriba!

MilesNielsen
fuente
1

Puedes escribir tu propia función:

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
Yuda Prawira
fuente
1
¿Agregar a las cadenas crea una nueva copia de la cadena?
Jeremy L
1
@Nerdling: sí, lo que puede conducir a algunas ineficiencias bastante impresionantes en las funciones de uso frecuente (o, de hecho, funciones de uso poco frecuente que actúan sobre grandes bloques de texto). Consulte esta página para obtener más detalles. : D
Jeremy Sandell
¿Prueba contra cadenas citadas? No.
Jimmy Kane el
1

Las soluciones con HTML-Parser son frágiles, si se ejecutan solo una vez:

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

resultados en:

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

lo que pretendes evitar si usa un analizador HTML, cuente las etiquetas hasta que se reemplacen cero:

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
Falk Nisius
fuente
1
Si llama a una función llamada html_to_texte incrusta el texto que sale de esa función dentro de html sin escapar de ese texto, entonces es la falta de escape, que es una vulnerabilidad de seguridad, no la html_to_textfunción. La html_to_textfunción nunca le prometió que el resultado sería texto. E insertar texto en html sin escapar es una vulnerabilidad de seguridad potencial, independientemente de si obtuvo el texto html_to_text u otra fuente.
kasperd
Tiene razón en el caso, de una falta de escape, pero la pregunta era quitar html de una cadena dada para no escapar de una cadena dada. Si las respuestas anteriores crean nuevos html con sus soluciones como resultado de eliminar algunos html, entonces el uso de estas soluciones es peligroso.
Falk Nisius
1

Esta es una solución rápida y puede optimizarse aún más, pero funcionará bien. Este código reemplazará todas las etiquetas no vacías con "" y eliminará todas las etiquetas html de un texto de entrada dado. Puede ejecutarlo usando ./file.py input output

    #!/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"
Kiran Mohan
fuente
1

Una adaptación a Python 3 de la respuesta de 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()
CpILL
fuente
1

Para un proyecto, necesitaba tan HTML, pero también css y js. Por lo tanto, hice una variación de la respuesta de Eloff:

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()
cola de ratón
fuente
1

Aquí hay una solución similar a la respuesta actualmente aceptada ( https://stackoverflow.com/a/925630/95989 ), excepto que usa la HTMLParserclase interna directamente (es decir, sin subclases), lo que la hace significativamente más breve:

def strip_html (texto):
    partes = []                                                                      
    analizador = HTMLParser ()                                                           
    parser.handle_data = parts.append                                               
    parser.feed (texto)                                                               
    volver '' .join (partes)
Ricardo
fuente
0

Estoy analizando los archivos Léame de Github y encuentro que lo siguiente realmente funciona bien:

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

Y entonces

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

Elimina todas las rebajas y html correctamente.

PascalVKooten
fuente
0

Usando BeautifulSoup, html2text o el código de @Eloff, la mayoría de las veces, quedan algunos elementos html, código javascript ...

Por lo tanto, puede usar una combinación de estas bibliotecas y eliminar el formato de descuento (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

Funciona bien para mí, pero puede mejorarse, por supuesto ...

hayj
fuente
0

Código simple! Esto eliminará todo tipo de etiquetas y contenido dentro de él.

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

Pero no dará un resultado completo si el texto contiene símbolos <> dentro de él.

Vanjith
fuente
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()
Dabingsou
fuente
-2

Este método funciona perfectamente para mí y no requiere instalaciones adicionales:

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).
Juan
fuente
3
Esto decodifica entidades HTML en texto plano, pero obviamente no elimina ninguna etiqueta, que era la pregunta original. (Además, el segundo bloque try-except debe ser sangrado para que el código haga lo mismo).
Søren Løvborg