Як виконати декодування / кодування HTML за допомогою Python / Django?


127

У мене є рядок, кодований 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, щоб браузер відображався у вигляді зображення, а не відображався як текст.

Рядок зберігається так, тому що я використовую інструмент для скребки, який називається BeautifulSoup, він "сканує" веб-сторінку і отримує з неї певний вміст, а потім повертає рядок у такому форматі.

Я знайшов, як це зробити в C #, але не в Python . Може хтось допоможе мені?

Пов'язані

Відповіді:


118

Враховуючи випадок використання Джанго, на це є дві відповіді. Ось його 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)

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

Як пропозиція: можливо, буде більше сенсу зберігати HTML без нагляду у вашій базі даних. Варто було б поглянути на отримання нездійснених результатів від BeautifulSoup, якщо можливо, і взагалі уникати цього процесу.

У режимі Django втеча відбувається лише під час надання шаблону; щоб запобігти втечі, просто скажіть двигуну-шаблону не уникати рядка. Для цього використовуйте один із цих параметрів у своєму шаблоні:

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

1
Чому б не використовувати Джанго чи Гепард?
Мат

4
Чи немає протилежності django.utils.html.escape?
Мат

12
Я думаю, що втеча відбувається лише в Джанго під час візуалізації шаблонів. Тому немає необхідності в пейзаж - ви просто скажете двигуну-шаблону не бігти. або {{context_var | safe}} або {% autoescape off%} {{context_var}} {% endautoescape%}
Daniel Naab

3
@Daniel: Будь ласка, змініть ваш коментар на відповідь, щоб я міг його проголосувати! | безпечно було саме те, що я (і я впевнений, що інші) шукав у відповідь на це питання.
Уейн Коортс

1
html.parser.HTMLParser().unescape()застаріло в 3.5. Використовуйте html.unescape()замість цього.
pjvandehaar

114

За допомогою стандартної бібліотеки:

  • HTML-втеча

    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
Я думаю, що це найпростіша «батарея» та правильна відповідь. Я не знаю, чому люди голосують за дію Джанго / Гепард.
Даніель Бактіар

Я теж вважаю це, за винятком того, що ця відповідь не здається повною. HTMLParserпотрібно підкласифікувати, сказати, що робити з усіма частинами будь-якого об'єкта, яким він подається, а потім подати об'єкт для розбору, як видно тут . Крім того, ви все одно хочете використовувати name2codepointdict для перетворення кожної html-ідентичності у фактичну таблицю, яку він представляє.
Марконій

Ти маєш рацію. Некласифікований HTMLParserне міг би працювати так, як ми хотіли, якщо в нього вставити сутність HTML. Можливо, я мушу перейменувати htmlparserйого _htmlparser, щоб приховати це, і лише розкрити unescapeметод таким, як функція помічника.
Цзяндже Чжан

3
Примітка за 2015 рік, HTMLParser.unescape застаріла в параграфе 3,4 і видаляється в 3,5. використовувати from html import unescapeзамість цього
Кароліс Ризеліс

2
Зауважте, що це не обробляє спеціальних символів, таких як німецькі Umlauts ("Ü")
576i

80

Для кодування html існує стандартна бібліотека cgi.escape :

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


20

Використовуйте рішення Даніеля, якщо набір закодованих символів відносно обмежений. В іншому випадку використовуйте одну з численних бібліотек для розбору 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!'

BeautifulSoup НЕ конвертувати шестигранні об'єкти (& # x65;) stackoverflow.com/questions/57708 / ...
JFS

1
Для BeautifulSoup4 еквівалентом було б:from bs4 import BeautifulSoup BeautifulSoup("Sacr&eacute; bl&#101;u!").contents[0]
радикад



6

Коментар Даніеля як відповідь:

"втеча відбувається лише в Django під час візуалізації шаблону. Тому немає необхідності в сканері - ви просто скажете двигуну шаблону не виходити. {{context_var | safe}} або {% autoescape off%} {{context_var}} { % endautoescape%} "


Працює, за винятком того, що моя версія "Джанго" не має "безпечного". Я використовую замість цього "втечу". Я припускаю, що це те саме.
willem

1
@willem: вони навпаки!
Ашера

5

Я знайшов чудову функцію за адресою: 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]

Перевага використання re полягає в тому, що ви можете відповідати обом & # 039; та & # 39; використовуючи той самий пошук.
Ніл Стублен

Це не обробляє, &#xA0;який повинен декодувати те саме, що &#160;і &nbsp;.
Майк Самуель

3

Якщо хтось шукає простий спосіб зробити це за допомогою шаблонів django, ви завжди можете використовувати такі фільтри:

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

У мене були дані, що надходять від постачальника, і все, що я розміщував, містив HTML-теги, фактично написані на відображеній сторінці, як ніби ви дивитесь на джерело. Наведений вище код мені дуже допоміг. Сподіваюся, що це допомагає іншим.

Ура !!


3

Хоча це справді давнє питання, це може спрацювати.

Джанго 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
Це був єдиний, хто зміг розшифрувати сурогатні пари, закодовані як html-сутності "&#55349;&#56996;". Потім за іншим result.encode('utf-16', 'surrogatepass').decode('utf-16')я нарешті отримав оригінальну спинку.
rescdsk

1

Я знайшов це у вихідному коді гепарду ( тут )

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

2
Список обернено, тому що заміни декодування та кодування завжди повинні проводитися симетрично. Без реверсу ви могли б, наприклад. перетворити "& amp; lt;" до '& lt;', то на наступному кроці неправильно перетворіть це у '<'.
bobince

1

Ви також можете використовувати django.utils.html.escape

from django.utils.html import escape

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

ОП запитували про невидимкування, а не про втечу.
клеймація

У назві itsellf він також попросив кодувати - просто знайшов вашу відповідь і я вдячний за це.
Саймон Штейнбергер

1
Не те, що запитувала ОП, але я вважаю це корисним.
прямокутник прямокутник

0

Нижче представлена ​​функція пітона, яка використовує модуль 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;"


0

Шукаючи найпростіше рішення цього питання в Django та Python, я виявив, що ви можете використовувати вбудовані в їх функції функції для втечі / 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" />'
)

Джанго

Вам потрібен Джанго> = 1,0

пейзаж

Щоб відмовитися від скребленого HTML-коду, ви можете використовувати django.utils.text.unescape_entities, які:

Перетворити всі іменовані та числові посилання символів у відповідні символи unicode.

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

Втеча

Щоб уникнути чистого коду HTML, ви можете використовувати django.utils.html.escape, який:

Повертає поданий текст із розширеннями, лапками та кутовими дужками, закодованими для використання в HTML.

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

Пітон

Вам потрібен Python> = 3.4

пейзаж

Для відновлення скасованого HTML-коду ви можете використовувати html.unescape який:

Перетворити всі посилання на імена і числові символьні (наприклад &gt;, &#62;, &x3e;) в рядок s в відповідні символи Unicode.

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

Втеча

Щоб уникнути чистого HTML-коду, ви можете використовувати html.escape, який:

Перетворення символів &, <і >в строкових з до HTML-безпечних послідовностей.

>>> from html import escape
>>> scraped_html == escape(clean_html)
True
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.