Wie führe ich eine HTML-Dekodierung / Kodierung mit Python / Django durch?


127

Ich habe eine Zeichenfolge, die HTML-codiert ist:

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

Ich möchte das ändern 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" /> 

Ich möchte, dass dies als HTML registriert wird, damit es vom Browser als Bild gerendert wird, anstatt als Text angezeigt zu werden.

Die Zeichenfolge wird so gespeichert, da ich ein Web-Scraping-Tool namens BeautifulSoup"scanne", eine Webseite "scannt" und bestimmte Inhalte daraus abruft. Anschließend wird die Zeichenfolge in diesem Format zurückgegeben.

Ich habe herausgefunden, wie das in C # geht, aber nicht in Python . Kann mir jemand helfen?

verbunden

Antworten:


118

Angesichts des Django-Anwendungsfalls gibt es zwei Antworten darauf. Hier ist seine django.utils.html.escapeFunktion als Referenz:

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

Um dies umzukehren, sollte die in Jakes Antwort beschriebene Cheetah-Funktion funktionieren, es fehlt jedoch das einfache Anführungszeichen. Diese Version enthält ein aktualisiertes Tupel, wobei die Reihenfolge des Ersetzens umgekehrt ist, um symmetrische Probleme zu vermeiden:

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)

Dies ist jedoch keine allgemeine Lösung; Es ist nur für Zeichenfolgen geeignet, die mit codiert sind django.utils.html.escape. Generell ist es eine gute Idee, sich an die Standardbibliothek zu halten:

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

Als Vorschlag: Es kann sinnvoller sein, den HTML-Code nicht in Ihrer Datenbank zu speichern. Es lohnt sich, wenn möglich zu versuchen, uneingeschränkte Ergebnisse von BeautifulSoup zurückzugewinnen und diesen Prozess insgesamt zu vermeiden.

Bei Django erfolgt das Escape nur während des Renderns von Vorlagen. Um ein Entkommen zu verhindern, weisen Sie die Template-Engine an, nicht aus Ihrer Saite zu entkommen. Verwenden Sie dazu eine der folgenden Optionen in Ihrer Vorlage:

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

1
Warum nicht Django oder Cheetah verwenden?
Mat

4
Gibt es kein Gegenteil von django.utils.html.escape?
Mat

12
Ich denke, Escape tritt nur in Django während des Renderns von Vorlagen auf. Daher ist keine Flucht erforderlich - Sie weisen den Template-Motor lediglich an, nicht zu entkommen. entweder {{context_var | safe}} oder {% autoescape off%} {{context_var}} {% endautoescape%}
Daniel Naab

3
@ Daniel: Bitte ändere deinen Kommentar in eine Antwort, damit ich darüber abstimmen kann! | safe war genau das, wonach ich (und ich bin sicher, andere) als Antwort auf diese Frage gesucht habe.
Wayne Koorts

1
html.parser.HTMLParser().unescape()ist in 3.5 veraltet. Verwenden Sie html.unescape()stattdessen.
pjvandehaar

114

Mit der Standardbibliothek:

  • 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
Ich denke, dies ist die einfachste, "Batterie enthalten" und richtige Antwort. Ich weiß nicht, warum die Leute diese Django / Cheetah-Sache wählen.
Daniel Baktiar

Ich denke auch, außer dass diese Antwort nicht vollständig zu sein scheint. HTMLParsermuss in eine Unterklasse eingeteilt werden, erklärt werden, was mit allen Teilen eines Objekts zu tun ist, dem es zugeführt wird, und dann das zu analysierende Objekt zugeführt werden, wie hier gezeigt . Außerdem möchten Sie weiterhin das name2codepointDiktat verwenden, um jede HTML-Identität in das tatsächliche Zeichen zu konvertieren, das sie darstellt.
Marconius

Du hast recht. Die nicht klassifizierten Elemente HTMLParserkonnten nicht wie gewünscht funktionieren, wenn wir eine HTML-Entität einfügen . Vielleicht sollte ich umbenennen htmlparser, _htmlparserum es auszublenden, und die unescapeMethode nur als Hilfsfunktion aussetzen .
Jiangge Zhang

3
HTMLParser.unescape ist ein Hinweis für das Jahr 2015 und in Version 3.4 veraltet und in Version 3.5 entfernt. Verwenden Sie from html import unescapestattdessen
Karolis Ryselis

2
Beachten Sie, dass dies keine Sonderzeichen wie deutsche Umlaute ("Ü")
576i

80

Für die HTML-Codierung gibt es cgi.escape aus der Standardbibliothek:

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

Für die HTML-Dekodierung verwende ich Folgendes:

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)

Für etwas komplizierteres benutze ich BeautifulSoup.


20

Verwenden Sie Daniels Lösung, wenn der Satz codierter Zeichen relativ begrenzt ist. Verwenden Sie andernfalls eine der zahlreichen HTML-Parsing-Bibliotheken.

Ich mag BeautifulSoup, weil es mit fehlerhaftem XML / HTML umgehen kann:

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

Für Ihre Frage gibt es ein Beispiel in ihrer Dokumentation

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

BeautifulSoup konvertiert keine Hex-Entitäten (& # x65;) stackoverflow.com/questions/57708/…
jfs

1
Für BeautifulSoup4 wäre das Äquivalent:from bs4 import BeautifulSoup BeautifulSoup("Sacr&eacute; bl&#101;u!").contents[0]
Radicand



6

Daniels Kommentar als Antwort:

"Escapeing tritt nur in Django während des Renderns von Vorlagen auf. Daher ist kein Unescape erforderlich. Sie weisen die Template-Engine lediglich an, nicht zu entkommen. Entweder {{context_var | safe}} oder {% autoescape off%} {{context_var}} { % endautoescape%} "


Funktioniert, außer dass meine Version von Django nicht "sicher" ist. Ich benutze stattdessen 'Escape'. Ich nehme an, es ist dasselbe.
Willem

1
@ Willem: Sie sind das Gegenteil!
Asherah

5

Ich habe eine gute Funktion gefunden unter: 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]

Der Vorteil der Verwendung von re besteht darin, dass Sie beide & # 039; und & # 39; mit der gleichen Suche.
Neal Stublen

Dies behandelt nicht, &#xA0;was auf das gleiche wie &#160;und dekodiert werden soll &nbsp;.
Mike Samuel

3

Wenn jemand nach einer einfachen Möglichkeit sucht, dies über die Django-Vorlagen zu tun, können Sie immer Filter wie diesen verwenden:

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

Ich hatte einige Daten von einem Anbieter und alles, was ich gepostet habe, hatte HTML-Tags, die tatsächlich auf die gerenderte Seite geschrieben wurden, als ob Sie sich die Quelle ansehen würden. Der obige Code hat mir sehr geholfen. Hoffe das hilft anderen.

Prost!!


3

Auch wenn dies eine sehr alte Frage ist, kann dies funktionieren.

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
Dies war die einzige, die Ersatzpaare dekodieren konnte, die als HTML-Entitäten codiert waren, wie z "&#55349;&#56996;". Dann nach dem anderen result.encode('utf-16', 'surrogatepass').decode('utf-16')hatte ich endlich das Original zurück.
Rescdsk

1

Ich fand dies im Cheetah-Quellcode ( hier )

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

Ich bin mir nicht sicher, warum sie die Liste umkehren. Ich denke, das hängt mit der Art und Weise zusammen, wie sie codieren. Bei Ihnen muss sie möglicherweise nicht rückgängig gemacht werden. Auch wenn ich du wäre, würde ich htmlCodes ändern, um eine Liste von Tupeln zu sein, anstatt eine Liste von Listen ... das geht aber in meiner Bibliothek :)

Ich habe bemerkt, dass Ihr Titel auch nach Codierung gefragt wurde. Hier ist die Codierungsfunktion von 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
Die Liste ist umgekehrt, da das Ersetzen von Dekodierungen und Kodierungen immer symmetrisch erfolgen muss. Ohne die Umkehrung könnte man zB. konvertieren '& amp; lt;' in '& lt;' umwandeln, dann im nächsten Schritt fälschlicherweise in '<' konvertieren.
Bobince

1

Sie können auch django.utils.html.escape verwenden

from django.utils.html import escape

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

OP fragte nach dem Ausweichen, nicht nach der Flucht.
Claymation

Im Titel selbst hat er auch um Kodierung gebeten - habe gerade deine Antwort gefunden und bin dafür dankbar.
Simon Steinberger

1
Nicht das, was das OP verlangt hat, aber ich fand das nützlich.
Rechteck

0

Unten finden Sie eine Python-Funktion, die ein Modul verwendet htmlentitydefs. Es ist nicht perfekt. Die Version htmlentitydefs, die ich habe, ist unvollständig und es wird davon ausgegangen, dass alle Entitäten zu einem Codepunkt dekodieren, was für Entitäten wie &NotEqualTilde;:

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

NotEqualTilde;     U+02242 U+00338    ≂̸

Mit diesen Einschränkungen ist hier der Code.

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

Dies ist die einfachste Lösung für dieses Problem -

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

Von dieser Seite .


0

Bei der Suche nach der einfachsten Lösung dieser Frage in Django und Python habe ich festgestellt, dass Sie integrierte Funktionen verwenden können, um HTML-Code zu maskieren / zu entfernen.

Beispiel

Ich habe Ihren HTML-Code gespeichert in scraped_htmlund 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

Sie benötigen Django> = 1.0

entkommen

Um unescape Ihre geschabt HTML - Code können Sie verwenden django.utils.text.unescape_entities , die:

Konvertieren Sie alle benannten und numerischen Zeichenreferenzen in die entsprechenden Unicode-Zeichen.

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

Flucht

Um Ihrem sauberen HTML-Code zu entkommen, können Sie django.utils.html.escape verwenden, das:

Gibt den angegebenen Text mit kaufmännischem Und, Anführungszeichen und spitzen Klammern zurück, die für die Verwendung in HTML codiert sind.

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

Python

Sie benötigen Python> = 3.4

entkommen

Um Ihren abgekratzten HTML-Code zu entfernen, können Sie html.unescape verwenden, das:

Wandeln Sie alle Namen und numerische Zeichenreferenzen (zB &gt;, &#62;, &x3e;) in der Zeichenkette s in den entsprechenden Unicode - Zeichen.

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

Flucht

Um Ihrem sauberen HTML-Code zu entkommen, können Sie html.escape verwenden, das:

Konvertieren Sie die Zeichen &, <und >in String s in HTML-safe - Sequenzen.

>>> from html import escape
>>> scraped_html == escape(clean_html)
True
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.