Как выполнить декодирование/кодирование 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 111

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 unescaped в вашей базе данных. Было бы полезно изучить возможность получения неотвратимых результатов от 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-теги на самом деле написано на странице, как если бы вы смотрели на источник. Приведенный выше код мне очень помог. Надеюсь, это поможет другим.

Ура!!

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

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

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

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, которая использует модуль 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

чтобы удалить ваш выскобленный 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

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

преобразование всех именованных и числовых ссылок символов (например,&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