Come eseguo la decodifica / codifica HTML usando Python / Django?


127

Ho una stringa codificata in 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" />'''

Voglio cambiarlo in:

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

Voglio che questo venga registrato come HTML in modo che venga visualizzato come immagine dal browser anziché essere visualizzato come testo.

La stringa viene memorizzata in questo modo perché sto usando uno strumento di scraping web chiamato BeautifulSoup, "scansiona" una pagina Web e ne ricava determinati contenuti, quindi restituisce la stringa in quel formato.

Ho trovato come farlo in C # ma non in Python . Qualcuno mi può aiutare?

Relazionato

Risposte:


118

Dato il caso d'uso Django, ci sono due risposte a questo. Ecco la sua django.utils.html.escapefunzione, per riferimento:

def escape(html):
    """Returns the given HTML with ampersands, quotes and carets encoded."""
    return mark_safe(force_unicode(html).replace('&', '&amp;').replace('<', '&l
t;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;'))

Per invertire ciò, la funzione Ghepardo descritta nella risposta di Jake dovrebbe funzionare, ma manca la virgoletta singola. Questa versione include una tupla aggiornata, con l'ordine di sostituzione invertito per evitare problemi simmetrici:

def html_decode(s):
    """
    Returns the ASCII decoded version of the given HTML string. This does
    NOT remove normal HTML tags like <p>.
    """
    htmlCodes = (
            ("'", '&#39;'),
            ('"', '&quot;'),
            ('>', '&gt;'),
            ('<', '&lt;'),
            ('&', '&amp;')
        )
    for code in htmlCodes:
        s = s.replace(code[1], code[0])
    return s

unescaped = html_decode(my_string)

Questa, tuttavia, non è una soluzione generale; è appropriato solo per le stringhe codificate con django.utils.html.escape. Più in generale, è una buona idea attenersi alla libreria standard:

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

# >= Python 3.5:
from html import unescape
unescaped = unescape(my_string)

Come suggerimento: potrebbe essere più sensato archiviare l'HTML senza caratteri di escape nel database. Vale la pena cercare di ottenere risultati non salvati da BeautifulSoup, se possibile, ed evitare del tutto questo processo.

Con Django, l'escaping si verifica solo durante il rendering del modello; quindi per evitare di scappare basta dire al motore di template di non sfuggire alla stringa. Per fare ciò, usa una di queste opzioni nel tuo modello:

{{ context_var|safe }}
{% autoescape off %}
    {{ context_var }}
{% endautoescape %}

1
Perché non usare Django o Cheetah?
Mat

4
Non c'è opposto di django.utils.html.escape?
Mat

12
Penso che la fuga avvenga solo in Django durante il rendering del modello. Pertanto, non è necessario uno scappamento: basta dire al motore dei modelli di non scappare. o {{context_var | safe}} o {% autoescape off%} {{context_var}} {% endautoescape%}
Daniel Naab

3
@Daniel: cambia il tuo commento in una risposta in modo che io possa votarlo! | safe era esattamente quello che io (e sono sicuro che gli altri) stavo cercando in risposta a questa domanda.
Wayne Koorts,

1
html.parser.HTMLParser().unescape()è deprecato in 3.5. Usa html.unescape()invece.
pjvandehaar,

114

Con la libreria standard:

  • HTML Escape

    try:
        from html import escape  # python 3.x
    except ImportError:
        from cgi import escape  # python 2.x
    
    print(escape("<"))
  • HTML Unescape

    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("&gt;"))

12
Penso che questa sia la risposta più semplice, "batteria inclusa" e corretta. Non so perché la gente voti quella cosa di Django / Cheetah.
Daniel Baktiar,

Lo penso anche io, tranne per il fatto che questa risposta non sembra completa. HTMLParserha bisogno di essere sottoclassato, dire cosa fare con tutte le parti di qualsiasi oggetto che viene alimentato e quindi alimentare l'oggetto da analizzare, come visto qui . Inoltre, vorrai comunque utilizzare name2codepointdict per convertire ogni identità html nel carattere effettivo che rappresenta.
Marconius,

Hai ragione. I non classificati HTMLParsernon potevano funzionare come avremmo voluto se avessimo inserito un'entità HTML al suo interno. Forse dovrei rinominarlo htmlparserper _htmlparsernasconderlo ed esporre il unescapemetodo solo come una funzione di supporto.
Jiangge Zhang,

3
Una nota per l'anno 2015, HTMLParser.unescape è deprecato in py 3.4 e rimosso in 3.5. usa from html import unescapeinvece
Karolis Ryselis l'

2
Nota che questo non gestisce personaggi speciali come German Umlauts ("Ü")
576i

80

Per la codifica HTML, c'è cgi.escape dalla libreria standard:

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

Per la decodifica html, utilizzo quanto segue:

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)

Per qualcosa di più complicato, uso BeautifulSoup.


20

Usa la soluzione di daniel se il set di caratteri codificati è relativamente limitato. In caso contrario, utilizzare una delle numerose librerie di analisi HTML.

Mi piace BeautifulSoup perché può gestire XML / HTML non valido:

http://www.crummy.com/software/BeautifulSoup/

per la tua domanda, c'è un esempio nella loro documentazione

from BeautifulSoup import BeautifulStoneSoup
BeautifulStoneSoup("Sacr&eacute; bl&#101;u!", 
                   convertEntities=BeautifulStoneSoup.HTML_ENTITIES).contents[0]
# u'Sacr\xe9 bleu!'

BeautifulSoup non converte entità esadecimali (& # x65;) stackoverflow.com/questions/57708/…
jfs

1
Per BeautifulSoup4, l'equivalente sarebbe:from bs4 import BeautifulSoup BeautifulSoup("Sacr&eacute; bl&#101;u!").contents[0]
radicand



6

Il commento di Daniel come risposta:

"l'escaping si verifica solo in Django durante il rendering del modello. Pertanto, non è necessario un unescape: basta dire al motore di template di non scappare. {{context_var | safe}} o {% autoescape off%} {{context_var}} { % endautoescape%} "


Funziona, tranne per il fatto che la mia versione di Django non ha "sicuro". Uso invece "escape". Presumo sia la stessa cosa.
Willem,

1
@willem: sono l'opposto!
Asherah,

5

Ho trovato una funzione eccellente su: 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]

Il vantaggio di usare re è che puoi abbinare entrambi & # 039; e & # 39; usando la stessa ricerca.
Neal Stublen,

Questo non gestisce &#xA0;quale dovrebbe essere decodificato alla stessa cosa di &#160;e &nbsp;.
Mike Samuel,

3

Se qualcuno è alla ricerca di un modo semplice per farlo tramite i modelli di django, puoi sempre usare filtri come questo:

<html>
{{ node.description|safe }}
</html>

Avevo alcuni dati provenienti da un fornitore e tutto ciò che ho pubblicato aveva tag html effettivamente scritti sulla pagina renderizzata come se stessi guardando la fonte. Il codice sopra mi ha aiutato molto. Spero che questo aiuti gli altri.

Saluti!!


3

Anche se questa è una domanda davvero vecchia, potrebbe funzionare.

Django 1.5.5

In [1]: from django.utils.text import unescape_entities
In [2]: unescape_entities('&lt;img class=&quot;size-medium wp-image-113&quot; style=&quot;margin-left: 15px;&quot; title=&quot;su1&quot; src=&quot;http://blah.org/wp-content/uploads/2008/10/su1-300x194.jpg&quot; alt=&quot;&quot; width=&quot;300&quot; height=&quot;194&quot; /&gt;')
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" />'

1
Questo è stato l'unico in grado di decodificare coppie surrogate codificate come entità html, come "&#55349;&#56996;". Poi, dopo l'altro result.encode('utf-16', 'surrogatepass').decode('utf-16'), ho finalmente avuto il dorso originale.
rescdsk,

1

Ho trovato questo nel codice sorgente del ghepardo ( qui )

htmlCodes = [
    ['&', '&amp;'],
    ['<', '&lt;'],
    ['>', '&gt;'],
    ['"', '&quot;'],
]
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

non sono sicuro del perché invertano l'elenco, penso che abbia a che fare con il modo in cui codificano, quindi con te potrebbe non aver bisogno di essere invertito. Anche se fossi in te cambierei htmlCodes per essere un elenco di tuple anziché un elenco di elenchi ... questo andrà nella mia libreria :)

ho notato che anche il tuo titolo ha richiesto la codifica, quindi ecco la funzione di codifica di 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

2
L'elenco è invertito perché la decodifica e la codifica delle sostituzioni devono sempre essere eseguite simmetricamente. Senza l'inversione si potrebbe ad es. converti "& amp; lt;" in "& lt;", quindi nel passaggio successivo convertilo erroneamente in "<".
Bobince,

1

Puoi anche usare django.utils.html.escape

from django.utils.html import escape

something_nice = escape(request.POST['something_naughty'])

OP ha chiesto di non fuggire, non di fuggire.
Claymation,

Nel titolo ha anche chiesto la codifica: ho appena trovato la tua risposta e te ne sono grato.
Simon Steinberger,

1
Non è quello che l'OP ha chiesto, ma l'ho trovato utile.
rettangolo del

0

Di seguito è una funzione Python che utilizza il modulo htmlentitydefs. Non è perfetto La versione htmlentitydefsche ho è incompleta e si presume che tutte le entità decodificano in un punto di codice che è sbagliato per entità come &NotEqualTilde;:

http://www.w3.org/TR/html5/named-character-references.html

NotEqualTilde;     U+02242 U+00338    ≂̸

Con questi avvertimenti, ecco il codice.

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)  # '&#10;' -> unichr(10)
    if hex_digits: return unichr(int(hex_digits, 16))
    decimal_digits = match.group(2)  # '&#x10;' -> unichr(0x10)
    if decimal_digits: return unichr(int(decimal_digits, 10))
    name = match.group(3)  # name is 'lt' when '&lt;' was matched.
    if name:
        decoding = (htmlentitydefs.name2codepoint.get(name)
            # Treat &GT; like &gt;.
            # This is wrong for &Gt; and &Lt; 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;"

0

Questa è la soluzione più semplice per questo problema:

{% autoescape on %}
   {{ body }}
{% endautoescape %}

Da questa pagina .


0

Cercando la soluzione più semplice di questa domanda in Django e Python ho scoperto che puoi usare le loro funzioni incorporate per sfuggire / annullare il codice HTML.

Esempio

Ho salvato il tuo codice html in scraped_htmle clean_html:

scraped_html = (
    '&lt;img class=&quot;size-medium wp-image-113&quot; '
    'style=&quot;margin-left: 15px;&quot; title=&quot;su1&quot; '
    'src=&quot;http://blah.org/wp-content/uploads/2008/10/su1-300x194.jpg&quot; '
    'alt=&quot;&quot; width=&quot;300&quot; height=&quot;194&quot; /&gt;'
)
clean_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" />'
)

Django

Hai bisogno di Django> = 1.0

unescape

Per annullare lo scappamento del codice HTML raschiato, puoi usare django.utils.text.unescape_entities che:

Converti tutti i riferimenti ai nomi e ai caratteri numerici nei corrispondenti caratteri unicode.

>>> from django.utils.text import unescape_entities
>>> clean_html == unescape_entities(scraped_html)
True

fuga

Per sfuggire al tuo codice HTML pulito puoi usare django.utils.html.escape che:

Restituisce il testo indicato con e commerciali, virgolette e parentesi angolari codificate per l'uso in HTML.

>>> from django.utils.html import escape
>>> scraped_html == escape(clean_html)
True

Pitone

È necessario Python> = 3.4

unescape

Per annullare lo scappamento del codice HTML scartato, puoi utilizzare html.unescape che:

Converti tutti i riferimenti ai caratteri nominali e numerici (ad es &gt;. &#62;, &x3e;) Nella stringa s nei caratteri unicode corrispondenti.

>>> from html import unescape
>>> clean_html == unescape(scraped_html)
True

fuga

Per sfuggire al tuo codice HTML pulito puoi usare html.escape che:

Convertire i personaggi &, <e >nella stringa s sequenze HTML-safe.

>>> from html import escape
>>> scraped_html == escape(clean_html)
True
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.