script scraping parser example conectar con como python html

scraping - python web



Eliminar HTML de cadenas en Python (21)

¡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 Regex: 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 dejarlas?

Una cosa es evitar que la gente <i>italicizing</i> cosas, sin dejar que flote por ahí. Pero es otra cosa tomar una entrada arbitraria y hacerla completamente inofensiva. La mayoría de las técnicas en esta página dejarán intactos 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 }} ? Todos los separadores de etiquetas de esta página dejarán pasar firstname = ''<a'' y lastname = ''href="http://evil.com/">'' (excepto @Medeiros!), porque no son etiquetas completas en su propia Eliminar las etiquetas HTML normales no es suficiente.

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

Absolutamente NO hay garantía de que la cadena resultante sea segura para HTML. Por lo tanto, NUNCA marque seguro el resultado de una llamada strip_tags sin escapar primero, por ejemplo, con escape() .

¡Sigue sus consejos!

Para eliminar etiquetas con HTMLParser, debes ejecutarlo varias veces.

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

Mira 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 los <!-- comments --> , dejándote con

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

Este problema se reveló al proyecto Django en marzo de 2014. Sus antiguos strip_tags eran esencialmente los mismos que la respuesta principal a esta pregunta. Su nueva versión básicamente lo ejecuta en un bucle hasta que ejecutarlo de nuevo 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 se escapa del resultado de strip_tags() .

Actualización del 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 ingresar un bucle infinito en la función strip_tags (). La versión fija se reproduce arriba. Más detalles aquí .

Buenas cosas para copiar o usar

El código de mi ejemplo no controla las entidades HTML: las versiones empaquetadas de Django y MarkupSafe sí lo hacen.

Mi código de ejemplo se MarkupSafe de la excelente biblioteca MarkupSafe para la prevención de secuencias de comandos entre sitios. Es conveniente y rápido (con aceleraciones de C a su versión nativa de Python). Se incluye en Google App Engine y es utilizado por Jinja2 (2.7 y versiones superiores) , Mako, Pylons y más. Funciona fácilmente con las plantillas Django de Django 1.7.

Las 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, podría copiar lo que necesita de este archivo .

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

¡Comprende las propiedades de tu tag stripper! Ejecutar pruebas fuzz en él! Aquí está el código que solía hacer la investigación para esta respuesta.

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

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

Al imprimir una línea en un archivo HTML, estoy tratando de encontrar una manera de mostrar solo los contenidos 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>'' imprime ''hola'', etc. ¿Cómo Uno va sobre hacer esto?


¿Por qué todos ustedes lo hacen de la manera difícil? Puede utilizar la función BeautifulSoup get_text() .

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)


Aquí hay una solución similar a la respuesta actualmente aceptada ( https://.com/a/925630/95989 ), excepto que usa la clase interna HTMLParser directamente (es decir, sin subclasificación), por lo que es significativamente más terso:

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


El paquete Beautiful Soup hace esto inmediatamente por ti.

from bs4 import BeautifulSoup soup = BeautifulSoup(html) text = soup.get_text() print(text)


Esta es una solución rápida y puede optimizarse aún más, pero funcionará bien. Este código reemplazará todas las etiquetas que no estén vacías con "" y elimina todas las etiquetas html que forman un texto de entrada determinado. Puede ejecutarlo usando la entrada de entrada ./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"


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


Estoy analizando las lecturas 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.


Hay una manera simple de 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ás interesado en la clase (sobre la depuración inteligente con python) te doy un enlace: http://www.udacity.com/overview/Course/cs259/CourseRev/1 . ¡Es gratis!

¡De nada! :)


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

Actualicé a Python 3.2.3 y me encontré con errores.

La solución, proporcionada here 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 corrección y por el código original de Eloff que se proporciona arriba.


Las soluciones con HTML-Parser se pueden romper, si se ejecutan solo una vez:

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

resultados en:

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

Lo que pretendes prevenir. Si usa un analizador HTML, cuente las etiquetas hasta que se reemplacen los 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


Necesitaba una forma de quitar las etiquetas y decodificar las entidades HTML a texto sin formato. 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 HTMLParseError puede provocar un HTMLParseError .
  • Las entidades HTML con nombre no válidas (como &#apos; que son válidas en XML y XHTML, pero no en HTML simple) causarán una excepción ValueError .
  • Las entidades HTML numéricas que especifican puntos de código fuera del rango Unicode aceptable por Python (como, en algunos sistemas, caracteres fuera del plano multilingüe básico ) causarán una excepción ValueError .

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

Ejemplo: &lt;script&gt;alert("Hello");&lt;/script&gt; se convertirá en <script>alert("Hello");</script> , que es 100% correcto, pero obviamente no es suficiente si el texto sin formato 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 ha eliminado el contenido HTML).

(Sin embargo, el OP preguntó sobre la impresión del resultado en la consola, en cuyo caso no es necesario que se escape 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).


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

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

Para aquellos que no entienden las expresiones regulares, esto busca una cadena <...> , donde el contenido interno está formado por uno o más caracteres ( + ) que no es un < . El ? significa que coincidirá con la cadena más pequeña que pueda encontrar. Por ejemplo, dado <p>Hello</p> , coincidirá con <''p> y </p> separado con el ? . Sin él, coincidirá con toda la cadena <..Hello..> .

Si no aparece la etiqueta < en html (p. Ej., 2 < 3 ), debe escribirse como una secuencia de escape &... todos modos, así que la ^< puede ser innecesaria.


Para un proyecto, necesitaba el código HTML, pero también css y js. Por lo tanto, hice una variación de la respuesta de 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()



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


Si desea eliminar todas las etiquetas HTML, la forma más sencilla 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 obtuve "RuntimeError: se excedió la profundidad máxima de recursión", lo que no sucedió con el bloque de código anterior.


Si necesita conservar las 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()


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

En Python 2

from HTMLParser import HTMLParser class MLStripper(HTMLParser): def __init__(self): self.reset() self.fed = [] def handle_data(self, d): 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()

Para Python 3

from html.parser import HTMLParser class MLStripper(HTMLParser): def __init__(self): self.reset() self.strict = False self.convert_charrefs= True self.fed = [] def handle_data(self, d): 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()

Nota : esto funciona solo para 3.1. Para 3.2 o superior, debe llamar a la función init de la clase padre. Ver Usar HTMLParser en Python 3.2


Una adaptación de 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://.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()


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

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

Si necesita más control sobre qué es exactamente el desinfectado antes de convertirlo en texto, puede utilizar el Limpiador lxml explícitamente al pasar las opciones que desea 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)


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

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