python - extender - extends django template tag
¿Cómo realizo decodificación/codificación HTML usando Python/Django? (14)
Tengo una cadena que está codificada en html:
<img class="size-medium wp-image-113"
style="margin-left: 15px;" title="su1"
src="http://blah.org/wp-content/uploads/2008/10/su1-300x194.jpg"
alt="" width="300" height="194" />
Quiero cambiar eso a:
<img class="size-medium wp-image-113" style="margin-left: 15px;"
title="su1" src="http://blah.org/wp-content/uploads/2008/10/su1-300x194.jpg"
alt="" width="300" height="194" />
Quiero que esto se registre como HTML para que el navegador lo represente como una imagen en lugar de mostrarse como texto.
He encontrado cómo hacer esto en C # pero no en Python. ¿Alguien me puede ayudar?
Gracias.
Editar: Alguien me preguntó por qué mis cadenas se almacenan así. Es porque estoy usando una herramienta de raspado web que "escanea" una página web y obtiene cierto contenido de ella. La herramienta (BeautifulSoup) devuelve la cadena en ese formato.
Relacionado
A continuación se muestra una función de python que utiliza el módulo htmlentitydefs
. No es perfecto La versión de htmlentitydefs
que tengo es incompleta y supone que todas las entidades decodifican en un punto de código que es incorrecto para entidades como ≂̸
:
http://www.w3.org/TR/html5/named-character-references.html
NotEqualTilde; U+02242 U+00338 ≂̸
Sin embargo, con esas advertencias, aquí está el código.
def decodeHtmlText(html):
"""
Given a string of HTML that would parse to a single text node,
return the text value of that node.
"""
# Fast path for common case.
if html.find("&") < 0: return html
return re.sub(
''&(?:#(?:x([0-9A-Fa-f]+)|([0-9]+))|([a-zA-Z0-9]+));'',
_decode_html_entity,
html)
def _decode_html_entity(match):
"""
Regex replacer that expects hex digits in group 1, or
decimal digits in group 2, or a named entity in group 3.
"""
hex_digits = match.group(1) # '' '' -> unichr(10)
if hex_digits: return unichr(int(hex_digits, 16))
decimal_digits = match.group(2) # '''' -> unichr(0x10)
if decimal_digits: return unichr(int(decimal_digits, 10))
name = match.group(3) # name is ''lt'' when ''<'' was matched.
if name:
decoding = (htmlentitydefs.name2codepoint.get(name)
# Treat > like >.
# This is wrong for ≫ and ≪ which HTML5 adopted from MathML.
# If htmlentitydefs included mappings for those entities,
# then this code will magically work.
or htmlentitydefs.name2codepoint.get(name.lower()))
if decoding is not None: return unichr(decoding)
return match.group(0) # Treat "&noSuchEntity;" as "&noSuchEntity;"
Aunque esta es una pregunta muy antigua, esto puede funcionar.
Django 1.5.5
In [1]: from django.utils.text import unescape_entities
In [2]: unescape_entities(''<img class="size-medium wp-image-113" style="margin-left: 15px;" title="su1" src="http://blah.org/wp-content/uploads/2008/10/su1-300x194.jpg" alt="" width="300" height="194" />'')
Out[2]: u''<img class="size-medium wp-image-113" style="margin-left: 15px;" title="su1" src="http://blah.org/wp-content/uploads/2008/10/su1-300x194.jpg" alt="" width="300" height="194" />''
Con la biblioteca estándar:
HTML Escape
try: from html import escape # python 3.x except ImportError: from cgi import escape # python 2.x print(escape("<"))
Unescape HTML
try: from html import unescape # python 3.4+ except ImportError: try: from html.parser import HTMLParser # python 3.x (<3.4) except ImportError: from HTMLParser import HTMLParser # python 2.x unescape = HTMLParser().unescape print(unescape(">"))
Dado el caso de uso de Django, hay dos respuestas a esto. Aquí está su función django.utils.html.escape
, para referencia:
def escape(html):
"""Returns the given HTML with ampersands, quotes and carets encoded."""
return mark_safe(force_unicode(html).replace(''&'', ''&'').replace(''<'', ''&l
t;'').replace(''>'', ''>'').replace(''"'', ''"'').replace("''", '''''))
Para revertir esto, la función Cheetah descrita en la respuesta de Jake debería funcionar, pero le falta la comilla simple. Esta versión incluye una tupla actualizada, con el orden de reposición invertido para evitar problemas simétricos:
def html_decode(s):
"""
Returns the ASCII decoded version of the given HTML string. This does
NOT remove normal HTML tags like <p>.
"""
htmlCodes = (
("''", '''''),
(''"'', ''"''),
(''>'', ''>''),
(''<'', ''<''),
(''&'', ''&'')
)
for code in htmlCodes:
s = s.replace(code[1], code[0])
return s
unescaped = html_decode(my_string)
Esto, sin embargo, no es una solución general; solo es apropiado para cadenas codificadas con django.utils.html.escape
. De manera más general, es una buena idea quedarse con la biblioteca estándar:
# Python 2.x:
import HTMLParser
html_parser = HTMLParser.HTMLParser()
unescaped = html_parser.unescape(my_string)
# Python 3.x:
import html.parser
html_parser = html.parser.HTMLParser()
unescaped = html_parser.unescape(my_string)
Como sugerencia: puede tener más sentido almacenar el HTML sin guardar en su base de datos. Valdría la pena intentar recuperar los resultados no guardados de BeautifulSoup, si es posible, y evitar este proceso por completo.
Con Django, el escape solo se produce durante la representación de la plantilla; así que para evitar escaparse, simplemente dígale al motor de plantillas que no se escape de su hilo. Para hacerlo, use una de estas opciones en su plantilla:
{{ context_var|safe }}
{% autoescape off %}
{{ context_var }}
{% endautoescape %}
El comentario de Daniel como respuesta:
"escaparse solo ocurre en Django durante el renderizado de la plantilla. Por lo tanto, no hay necesidad de unescape: simplemente dile al motor de plantillas que no escape. {{context_var | safe}} o {% autoescape off%} {{context_var}} { % endautoescape%} "
En Python 3.4+:
import html
html.unescape(your_string)
Encontré esto en el código fuente de Cheetah ( here )
htmlCodes = [
[''&'', ''&''],
[''<'', ''<''],
[''>'', ''>''],
[''"'', ''"''],
]
htmlCodesReversed = htmlCodes[:]
htmlCodesReversed.reverse()
def htmlDecode(s, codes=htmlCodesReversed):
""" Returns the ASCII decoded version of the given HTML string. This does
NOT remove normal HTML tags like <p>. It is the inverse of htmlEncode()."""
for code in codes:
s = s.replace(code[1], code[0])
return s
No estoy seguro de por qué invierten la lista, creo que tiene que ver con la forma en que codifican, por lo que puede que no sea necesario revertirla. Además, si yo fuera tú, cambiaría htmlCodes para que sea una lista de tuplas en lugar de una lista de listas ... aunque esto va en mi biblioteca :)
noté que tu título también pedía codificación, así que aquí está la función de codificación de Cheetah.
def htmlEncode(s, codes=htmlCodes):
""" Returns the HTML encoded version of the given string. This is useful to
display a plain ASCII text string on a web page."""
for code in codes:
s = s.replace(code[0], code[1])
return s
Encontré una buena función en: http://snippets.dzone.com/posts/show/4569
def decodeHtmlentities(string):
import re
entity_re = re.compile("&(#?)(/d{1,5}|/w{1,8});")
def substitute_entity(match):
from htmlentitydefs import name2codepoint as n2cp
ent = match.group(2)
if match.group(1) == "#":
return unichr(int(ent))
else:
cp = n2cp.get(ent)
if cp:
return unichr(cp)
else:
return match.group()
return entity_re.subn(substitute_entity, string)[0]
Esta es la solución más fácil para este problema:
{% autoescape on %}
{{ body }}
{% endautoescape %}
De esta página .
Para la codificación html, hay cgi.escape de la biblioteca estándar:
>> help(cgi.escape)
cgi.escape = escape(s, quote=None)
Replace special characters "&", "<" and ">" to HTML-safe sequences.
If the optional flag quote is true, the quotation mark character (")
is also translated.
Para la decodificación html, uso lo siguiente:
import re
from htmlentitydefs import name2codepoint
# for some reason, python 2.5.2 doesn''t have this one (apostrophe)
name2codepoint[''#39''] = 39
def unescape(s):
"unescape HTML code refs; c.f. http://wiki.python.org/moin/EscapingHtml"
return re.sub(''&(%s);'' % ''|''.join(name2codepoint),
lambda m: unichr(name2codepoint[m.group(1)]), s)
Para algo más complicado, uso BeautifulSoup.
Si alguien está buscando una forma sencilla de hacerlo a través de las plantillas django, siempre puede usar filtros como este:
<html>
{{ node.description|safe }}
</html>
Recibí algunos datos de un proveedor y todo lo que publiqué tenía etiquetas html escritas realmente en la página representada como si estuvieras mirando la fuente. El código anterior me ayudó mucho. Espero que esto ayude a otros.
¡¡Aclamaciones!!
También puedes usar django.utils.html.escape
from django.utils.html import escape
something_nice = escape(request.POST[''something_naughty''])
Use la solución de Daniel si el conjunto de caracteres codificados es relativamente restringido. De lo contrario, use una de las numerosas bibliotecas de análisis de HTML.
Me gusta BeautifulSoup porque puede manejar XML / HTML mal formado:
http://www.crummy.com/software/BeautifulSoup/
para su pregunta, hay un ejemplo en su documentation
from BeautifulSoup import BeautifulStoneSoup
BeautifulStoneSoup("Sacré bleu!",
convertEntities=BeautifulStoneSoup.HTML_ENTITIES).contents[0]
# u''Sacr/xe9 bleu!''
Ver en la parte inferior de esta página en la wiki de Python , hay al menos 2 opciones para "unescape" html.