Bagaimana cara saya melakukan decoding / encoding HTML menggunakan Python / Django?

127

Saya memiliki string yang disandikan 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" />'''

Saya ingin mengubahnya menjadi:

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

Saya ingin ini mendaftar sebagai HTML sehingga ditampilkan sebagai gambar oleh browser alih-alih ditampilkan sebagai teks.

String disimpan seperti itu karena saya menggunakan alat pengikis web yang disebut BeautifulSoup, "memindai" halaman web dan mendapatkan konten tertentu darinya, lalu mengembalikan string dalam format itu.

Saya telah menemukan cara melakukan ini dalam C # tetapi tidak dalam Python . Adakah yang bisa membantu saya?

Terkait

rksprst
sumber

Jawaban:

118

Mengingat kasus penggunaan Django, ada dua jawaban untuk ini. Berikut django.utils.html.escapefungsinya, untuk referensi:

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

Untuk membalikkan ini, fungsi Cheetah yang dijelaskan dalam jawaban Jake harus bekerja, tetapi tidak memiliki tanda kutip tunggal. Versi ini mencakup tuple yang diperbarui, dengan urutan penggantian terbalik untuk menghindari masalah simetris:

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)

Namun, ini bukan solusi umum; hanya cocok untuk string yang dikodekan dengan django.utils.html.escape. Secara umum, itu adalah ide yang baik untuk tetap menggunakan perpustakaan standar:

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

Sebagai saran: mungkin lebih masuk akal untuk menyimpan HTML yang tidak terhapus di database Anda. Akan bermanfaat untuk mendapatkan hasil yang tidak terhindar dari BeautifulSoup jika memungkinkan, dan menghindari proses ini sama sekali.

Dengan Django, melarikan diri hanya terjadi selama rendering template; jadi untuk mencegah melarikan diri Anda hanya memberi tahu mesin templating untuk tidak melarikan diri dari tali Anda. Untuk melakukannya, gunakan salah satu opsi ini di templat Anda:

{{ context_var|safe }}
{% autoescape off %}
    {{ context_var }}
{% endautoescape %}
Daniel Naab
sumber
1
Mengapa tidak menggunakan Django atau Cheetah?
Mat
4
Apakah tidak ada lawan dari django.utils.html.escape?
Mat
12
Saya pikir melarikan diri hanya terjadi di Django selama rendering template. Oleh karena itu, tidak perlu melakukan unescape - Anda cukup memberi tahu mesin templating untuk tidak melarikan diri. baik {{context_var | safe}} atau {% autoescape off%} {{context_var}} {% endautoescape%}
Daniel Naab
3
@Daniel: Silakan ubah komentar Anda menjadi jawaban sehingga saya dapat memilihnya! | safe persis apa yang saya (dan saya yakin orang lain) cari dalam menjawab pertanyaan ini.
Wayne Koorts
1
html.parser.HTMLParser().unescape()tidak digunakan lagi dalam 3.5. Gunakan html.unescape()sebagai gantinya.
pjvandehaar
114

Dengan perpustakaan standar:

  • 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;"))
Jiangge Zhang
sumber
12
Saya pikir ini adalah jawaban yang paling mudah, 'termasuk baterai' dan benar. Saya tidak tahu mengapa orang memilih hal Django / Cheetah itu.
Daniel Baktiar
Saya juga berpikir begitu, kecuali bahwa jawaban ini sepertinya tidak lengkap. HTMLParserperlu subkelas, diberi tahu apa yang harus dilakukan dengan semua bagian dari objek yang diberi makan, dan kemudian memberi makan objek yang akan diuraikan, seperti yang terlihat di sini . Selain itu, Anda masih ingin menggunakan name2codepointdikt untuk mengkonversi setiap identitas html ke karakter aktual yang diwakilinya.
Marconius
Kamu benar. Unsubclassed HTMLParsertidak bisa berfungsi seperti yang kita inginkan jika kita memasukkan entitas HTML ke dalamnya. Mungkin saya harus mengganti nama htmlparseruntuk _htmlparsermenyembunyikannya, dan hanya mengekspos unescapemetode menjadi seperti fungsi pembantu.
Jiangge Zhang
3
Catatan untuk tahun 2015, HTMLParser.unescape tidak digunakan lagi di py 3.4 dan dihapus di 3.5. gunakan from html import unescapesebagai gantinya
Karolis Ryselis
2
Perhatikan bahwa ini tidak menangani karakter khusus seperti German Umlauts ("Ü")
576i
80

Untuk pengkodean html, ada cgi.escape dari perpustakaan standar:

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

Untuk decoding html, saya menggunakan yang berikut:

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)

Untuk hal yang lebih rumit, saya menggunakan BeautifulSoup.

pengguna26294
sumber
20

Gunakan solusi daniel jika rangkaian karakter yang disandikan relatif terbatas. Jika tidak, gunakan salah satu dari banyak parsing pustaka HTML.

Saya suka BeautifulSoup karena dapat menangani XML / HTML yang cacat:

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

untuk pertanyaan Anda, ada contoh dalam dokumentasi mereka

from BeautifulSoup import BeautifulStoneSoup
BeautifulStoneSoup("Sacr&eacute; bl&#101;u!", 
                   convertEntities=BeautifulStoneSoup.HTML_ENTITIES).contents[0]
# u'Sacr\xe9 bleu!'
vincent
sumber
BeautifulSoup tidak mengonversi entitas hex (& # x65;) stackoverflow.com/questions/57708/…
jfs
1
Untuk BeautifulSoup4, yang setara adalah:from bs4 import BeautifulSoup BeautifulSoup("Sacr&eacute; bl&#101;u!").contents[0]
radicand
10

Dengan Python 3.4+:

import html

html.unescape(your_string)
Collin Anderson
sumber
8

Lihat di bagian bawah halaman ini di Python wiki , setidaknya ada 2 opsi untuk "unescape" html.

zgoda
sumber
6

Komentar Daniel sebagai jawaban:

"melarikan diri hanya terjadi di Django selama rendering templat. Oleh karena itu, tidak diperlukan unescape - Anda cukup memberi tahu mesin templating untuk tidak melarikan diri. {{context_var | safe}} atau {% autoescape off%} {{context_var}} { % endautoescape%} "

makan siang
sumber
Bekerja, kecuali bahwa versi saya Django tidak memiliki 'aman'. Saya menggunakan 'melarikan diri' sebagai gantinya. Saya menganggap itu hal yang sama.
willem
1
@willem: mereka sebaliknya!
Asherah
5

Saya menemukan fungsi yang bagus di: 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]
lamban
sumber
Manfaat menggunakan re adalah Anda dapat mencocokkan keduanya & # 039; dan & # 39; menggunakan pencarian yang sama.
Neal Stublen
Ini tidak menangani &#xA0;yang harus diterjemahkan ke hal yang sama seperti &#160;dan &nbsp;.
Mike Samuel
3

Jika ada yang mencari cara sederhana untuk melakukannya melalui templat Django, Anda selalu dapat menggunakan filter seperti ini:

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

Saya memiliki beberapa data yang berasal dari vendor dan semua yang saya posting memiliki tag html yang sebenarnya ditulis pada halaman yang diberikan seolah-olah Anda sedang melihat sumbernya. Kode di atas sangat membantu saya. Semoga ini bisa membantu orang lain.

Bersulang!!

Chris Harty
sumber
3

Meskipun ini adalah pertanyaan yang sangat lama, ini mungkin berhasil.

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" />'
James
sumber
1
Ini adalah satu-satunya yang bisa memecahkan kode pasangan pengganti yang dikodekan sebagai entitas html, seperti "&#55349;&#56996;". Kemudian demi satu result.encode('utf-16', 'surrogatepass').decode('utf-16'), akhirnya saya mendapatkan kembali yang asli.
rescdsk
1

Saya menemukan ini di kode sumber Cheetah (di sini )

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

tidak yakin mengapa mereka membalikkan daftar, saya pikir itu ada hubungannya dengan cara mereka menyandikan, jadi dengan Anda itu mungkin tidak perlu dibalik. Juga jika saya jadi Anda, saya akan mengubah htmlCodes menjadi daftar tuple daripada daftar daftar ... ini terjadi di perpustakaan saya :)

saya perhatikan judul Anda meminta encode juga, jadi di sini adalah fungsi encode 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
Jake
sumber
2
Daftar dibalik karena penggantian decode dan encode selalu harus dibuat secara simetris. Tanpa membalikkan Anda bisa mis. ubah '& amp; lt;' ke '& lt;', maka pada langkah berikutnya salah mengonversi itu menjadi '<'.
bobince
1

Anda juga dapat menggunakan django.utils.html.escape

from django.utils.html import escape

something_nice = escape(request.POST['something_naughty'])
Seth Gottlieb
sumber
OP bertanya tentang unescaping, tidak melarikan diri.
claymation
Dalam judulnya, dia juga meminta pengkodean - baru saja menemukan jawaban Anda dan berterima kasih untuk itu.
Simon Steinberger
1
Bukan apa yang diminta OP, tapi saya menemukan ini berguna.
persegi panjang
0

Di bawah ini adalah fungsi python yang menggunakan modul htmlentitydefs. Itu tidak sempurna. Versi htmlentitydefsyang saya miliki tidak lengkap dan mengasumsikan bahwa semua entitas decode ke satu codepoint yang salah untuk entitas seperti &NotEqualTilde;:

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

NotEqualTilde;     U+02242 U+00338    ≂̸

Dengan peringatan itu, inilah kodenya.

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;"
Mike Samuel
sumber
0

Ini adalah solusi termudah untuk masalah ini -

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

Dari halaman ini .

smilitude
sumber
0

Mencari solusi paling sederhana dari pertanyaan ini dalam Django dan Python saya temukan Anda dapat menggunakan fungsi builtin untuk melarikan diri / menghapus kode html.

Contoh

Saya menyimpan kode html Anda di scraped_htmldan 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

Anda membutuhkan Django> = 1.0

hapus jejak

Untuk menghapus kode html tergores Anda, Anda dapat menggunakan django.utils.text.unescape_entities yang:

Konversi semua referensi nama dan karakter numerik ke karakter unicode yang sesuai.

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

melarikan diri

Untuk menghindari kode html bersih Anda, Anda dapat menggunakan django.utils.html.escape yang:

Mengembalikan teks yang diberikan dengan ampersand, tanda kutip dan kurung sudut yang disandikan untuk digunakan dalam HTML.

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

Python

Anda membutuhkan Python> = 3.4

hapus jejak

Untuk menghapus kode html tergores Anda, Anda dapat menggunakan html.unescape yang:

Mengkonversi semua referensi nama dan numerik karakter (misalnya &gt;, &#62;, &x3e;) dalam string s dengan karakter unicode yang sesuai.

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

melarikan diri

Untuk menghindari kode html bersih Anda, Anda dapat menggunakan html.escape yang:

Konversikan karakter &, <dan >dalam string s ke urutan aman-HTML.

>>> from html import escape
>>> scraped_html == escape(clean_html)
True
Paolo Melchiorre
sumber