Как выполнить декодирование/кодирование HTML с помощью Python / Django?

у меня есть строка, закодированная в 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" />

Я хочу изменить это на:

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

Я хочу, чтобы это регистрировалось как HTML, чтобы он отображался как изображение браузером, а не отображался как текст.

Я нашел, как это сделать в C#, но не в Python. Кто-нибудь может мне помочь?

спасибо.

Edit: кто-то спросил, почему мои строки хранятся так. Это потому, что я использую инструмент веб-выскабливания, который "сканирует" веб-страницу и получает от нее определенный контент. Инструмент (BeautifulSoup) возвращает строку в этом формате.

по теме

15 ответов


учитывая случай использования Django, есть два ответа на это. Вот ее django.utils.html.escape функции для справки:

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

чтобы отменить это, функция гепарда, описанная в ответе Джейка, должна работать, но отсутствует одинарная цитата. Эта версия включает в себя обновленный кортеж, с порядком замены отменен, чтобы избежать симметричных проблем:

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)

это, однако, не является общим решением; это подходит только для строк, закодированных с django.utils.html.escape. Больше как правило, рекомендуется придерживаться стандартной библиотеки:

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

как предложение: возможно, имеет смысл сохранить HTML без эскапады в вашей базе данных. Было бы стоит посмотреть на получение неотвратимых результатов от BeautifulSoup, если это возможно, и вообще избежать этого процесса.

С Django, экранирование происходит только во время рендеринга шаблона; поэтому, чтобы предотвратить экранирование, вы просто скажите шаблонному движку не избегать вашей строки. Для этого используйте один из эти параметры в вашем шаблоне:

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

со стандартной библиотекой:

  • 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;"))
    

для кодирования html есть cgi.побег из стандартной библиотеки:

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

для декодирования html я использую следующее:

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)

для чего-то более сложного я использую BeautifulSoup.


используйте решение Даниэля, если набор закодированных символов относительно ограничен. В противном случае используйте одну из многочисленных библиотек HTML-синтаксического анализа.

Мне нравится BeautifulSoup, потому что он может обрабатывать искаженный XML/HTML :

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

для вашего вопроса есть пример в их документация

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

смотрите в нижней части этого страница в Python wiki, есть по крайней мере 2 варианта "unescape" html.


В Python 3.4+:

import html

html.unescape(your_string)

комментарий Даниэля в качестве ответа:

"экранирование происходит только в Django во время рендеринга шаблона. Поэтому нет необходимости в unescape - вы просто говорите шаблонному движку не убегать. либо {{ context_var / safe }}, либо {% autoescape off %}{{ context_var }}{% endautoescape %}"


Я нашел прекрасную функцию в: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]

Если кто-то ищет простой способ сделать это с помощью шаблонов django, вы всегда можете использовать такие фильтры:

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

У меня были некоторые данные, поступающие от поставщика, и все, что я написал было HTML-теги на самом деле написано на странице, как если бы вы смотрели на источник. Приведенный выше код очень помог мне. Надеюсь, это поможет другим.

Ура!!


хотя это действительно старый вопрос, это может сработать.

Джанго 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" />'

Я нашел это в исходном коде гепарда (здесь)

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

не уверен, почему они отменяют список, Я думаю, это связано с тем, как они кодируют, поэтому с вами это может не понадобиться. Кроме того, на вашем месте я бы изменил htmlCodes на список кортежей, а не список списков... это происходит в моей библиотеке, хотя:)

Я заметил, что ваш заголовок тоже попросил кодировать, поэтому вот функция кодирования гепарда.

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

вы также можете использовать django.а utils.формат html.побег

from django.utils.html import escape

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

Ниже приведена функция python, которая использует module htmlentitydefs. Это не идеально. Версия htmlentitydefs то, что у меня есть, является неполным, и предполагается, что все сущности декодируются в одну кодовую точку, которая неверна для таких сущностей, как &NotEqualTilde;:

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

NotEqualTilde;     U+02242 U+00338    ≂̸

С этими оговорками, хотя, вот код.

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

Это самое простое решение этой проблемы -

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

С на этой странице.


Поиск простейшего решения этого вопроса в Django и Python я нашел, что вы можете использовать встроенные функции для escape / unescape html-кода.

пример

Я сохранил ваш html-код в scraped_html и 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 > = 1.0

unescape

для unescape ваш выскобленный html код вы можете использовать Джанго.а utils.текст.unescape_entities который:

преобразуйте все именованные и числовые символьные ссылки в соответствующие символы юникода.

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

побег

чтобы избежать чистого html-кода, Вы можете использовать Джанго.а utils.формат html.побег которых:

возвращает данный текст с амперсандами, кавычками и угловыми скобками, закодированными для использования в HTML.

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

Python

вам нужно Python >= 3.4

unescape

для unescape ваш выскобленный html код вы можете использовать HTML-код.unescape которых:

преобразовать все именованные и числовые ссылки символов (например,&gt;, &#62;, &x3e;) в строке s к соответствующим символам Юникода.

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

побег

чтобы избежать чистого html-кода, Вы можете использовать HTML-код.побег которых:

преобразовать символы &, < и > в строке s в HTML-безопасные последовательности.

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