¿Decodificar entidades HTML en una cadena Python?

266

Estoy analizando algo de HTML con Beautiful Soup 3, pero contiene entidades HTML que Beautiful Soup 3 no decodifica automáticamente para mí:

>>> from BeautifulSoup import BeautifulSoup

>>> soup = BeautifulSoup("<p>&pound;682m</p>")
>>> text = soup.find("p").string

>>> print text
&pound;682m

¿Cómo puedo decodificar las entidades HTML textpara obtener en "£682m"lugar de "&pound;682m".

jkp
fuente

Respuestas:

521

Python 3.4+

Uso html.unescape():

import html
print(html.unescape('&pound;682m'))

FYI html.parser.HTMLParser.unescapeestá en desuso y se suponía que debía eliminarse en 3.5 , aunque se dejó por error. Se eliminará del idioma pronto.


Python 2.6-3.3

Puede usar HTMLParser.unescape()desde la biblioteca estándar:

>>> try:
...     # Python 2.6-2.7 
...     from HTMLParser import HTMLParser
... except ImportError:
...     # Python 3
...     from html.parser import HTMLParser
... 
>>> h = HTMLParser()
>>> print(h.unescape('&pound;682m'))
£682m

También puede usar la sixbiblioteca de compatibilidad para simplificar la importación:

>>> from six.moves.html_parser import HTMLParser
>>> h = HTMLParser()
>>> print(h.unescape('&pound;682m'))
£682m
luc
fuente
9
este método no parece escapar de caracteres como "& # 8217;" en el motor de aplicaciones de google, aunque funciona localmente en python2.6. Todavía decodifica entidades (como & quot;) al menos
gfxmonk
¿Cómo se puede desaprobar una API no documentada? Editó la respuesta.
Markus Unterwaditzer
@MarkusUnterwaditzer no hay razón para que un método indocumentado no pueda ser desaprobado. Este arroja advertencias de desaprobación: vea mi edición de la respuesta.
Mark Amery
Parecería más lógico que, en lugar de solo el unescapemétodo, HTMLParserse desaprobara todo el módulo a favor html.parser.
Tom Russell
Vale la pena señalar para Python 2: los caracteres especiales se reemplazan con sus equivalentes de codificación Latin-1 (ISO-8859-1). Por ejemplo, puede ser necesario h.unescape(s).encode("utf-8"). Los documentos: "" "La definición provista aquí contiene todas las entidades definidas por XHTML 1.0 que pueden manejarse usando una simple sustitución textual en el conjunto de caracteres Latin-1 (ISO-8859-1)" ""
cobarde anónimo
65

Beautiful Soup maneja la conversión de entidades. En Beautiful Soup 3, deberá especificar el convertEntitiesargumento al BeautifulSoupconstructor (consulte la sección 'Conversión de entidades' de los documentos archivados). En Beautiful Soup 4, las entidades se decodifican automáticamente.

Hermosa sopa 3

>>> from BeautifulSoup import BeautifulSoup
>>> BeautifulSoup("<p>&pound;682m</p>", 
...               convertEntities=BeautifulSoup.HTML_ENTITIES)
<p682m</p>

Hermosa sopa 4

>>> from bs4 import BeautifulSoup
>>> BeautifulSoup("<p>&pound;682m</p>")
<html><body><p682m</p></body></html>
Ben James
fuente
+1. No tengo idea de cómo me perdí esto en los documentos: gracias por la información. Voy a aceptar la respuesta de Luc porque usa la biblioteca estándar que especifiqué en la pregunta (no es importante para mí) y probablemente sea de uso más general para otras personas.
jkp
55
BeautifulSoup4usos HTMLParser, en su mayoría. Ver la fuente
scharfmn
44
¿Cómo obtenemos la conversión en Beautiful Soup 4 sin todo el HTML extraño que no era parte de la cadena original? (es decir, <html> y <body>)
Praxiteles
@Praxiteles: BeautifulSoup ('& pound; 682m', "html.parser") stackoverflow.com/a/14822344/4376342
Soitje
13

Puede usar replace_entities de la biblioteca w3lib.html

In [202]: from w3lib.html import replace_entities

In [203]: replace_entities("&pound;682m")
Out[203]: u'\xa3682m'

In [204]: print replace_entities("&pound;682m")
£682m
Corvax
fuente
2

Beautiful Soup 4 te permite configurar un formateador a tu salida

Si pasa formatter=None, Beautiful Soup no modificará las cadenas en absoluto en la salida. Esta es la opción más rápida, pero puede llevar a que Beautiful Soup genere HTML / XML no válido, como en estos ejemplos:

print(soup.prettify(formatter=None))
# <html>
#  <body>
#   <p>
#    Il a dit <<Sacré bleu!>>
#   </p>
#  </body>
# </html>

link_soup = BeautifulSoup('<a href="http://example.com/?foo=val1&bar=val2">A link</a>')
print(link_soup.a.encode(formatter=None))
# <a href="http://example.com/?foo=val1&bar=val2">A link</a>
LoicUV
fuente
Esto no responde la pregunta. (Además, no tengo idea de lo que dicen los documentos que no es válido sobre el último bit de HTML aquí.)
Mark Amery
<< Sacré bleu! >> es la parte no válida, ya que tiene <y> sin escape y romperá el html a su alrededor. Sé que esta es una publicación tardía mía, pero en caso de que alguien esté mirando y se pregunte ...
GMasucci
0

Tuve un problema de codificación similar. Usé el método normalize (). Estaba recibiendo un error Unicode usando el método pandas .to_html () al exportar mi marco de datos a un archivo .html en otro directorio. Terminé haciendo esto y funcionó ...

    import unicodedata 

El objeto de marco de datos puede ser lo que quieras, llamémoslo tabla ...

    table = pd.DataFrame(data,columns=['Name','Team','OVR / POT'])
    table.index+= 1

codifique los datos de la tabla para que podamos exportarlos a un archivo .html en la carpeta de plantillas (esta puede ser la ubicación que desee :))

     #this is where the magic happens
     html_data=unicodedata.normalize('NFKD',table.to_html()).encode('ascii','ignore')

exportar cadena normalizada a archivo html

    file = open("templates/home.html","w") 

    file.write(html_data) 

    file.close() 

Referencia: documentación unicodedata

Alex
fuente
-4

Esto probablemente no es relevante aquí. Pero para eliminar estas entidades html de un documento completo, puede hacer algo como esto: (Asumir documento = página y perdone el código descuidado, pero si tiene ideas sobre cómo mejorarlo, soy todo oídos - Soy nuevo en esta).

import re
import HTMLParser

regexp = "&.+?;" 
list_of_html = re.findall(regexp, page) #finds all html entites in page
for e in list_of_html:
    h = HTMLParser.HTMLParser()
    unescaped = h.unescape(e) #finds the unescaped value of the html entity
    page = page.replace(e, unescaped) #replaces html entity with unescaped value
Neil Aggarwal
fuente
77
¡No! No necesita hacer coincidir las entidades HTML usted mismo y recorrerlas; .unescape()hace que para usted . No entiendo por qué usted y Rob han publicado estas soluciones demasiado complicadas que incluyen su propia coincidencia de entidades cuando la respuesta aceptada ya muestra claramente que .unescape()pueden encontrar entidades en la cadena.
Mark Amery