У меня есть строка, кодированная 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, чтобы оно отображалось как изображение браузером, а не отображалось как текст.
Я нашел, как это сделать на С#, но не на Python. Может кто-нибудь помочь мне?
Спасибо.
Изменить: кто-то спросил, почему мои строки хранятся так. Это потому, что я использую инструмент для веб-скрепок, который "сканирует" веб-страницу и получает от нее определенный контент. Инструмент (BeautifulSoup) возвращает строку в этом формате.
Учитывая вариант использования 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('&', '&').replace('<', '&l
t;').replace('>', '>').replace('"', '"').replace("'", '''))
Чтобы изменить это, функция Cheetah, описанная в ответе Джейка, должна работать, но в ней отсутствует одинарная кавычка. Эта версия включает в себя обновленный кортеж, порядок замены которого изменен, чтобы избежать симметричных проблем:
def html_decode(s):
"""
Returns the ASCII decoded version of the given HTML string. This does
NOT remove normal HTML tags like <p>.
"""
htmlCodes = (
("'", '''),
('"', '"'),
('>', '>'),
('<', '<'),
('&', '&')
)
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 %}
Со стандартной библиотекой:
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(">"))
HTMLParser
нужно разделить на подклассы, HTMLParser
, что делать со всеми частями любого объекта, для которого он подается, а затем передать объект для анализа, как показано здесь . Кроме того, вы все равно захотите использовать dict name2codepoint
для преобразования каждого идентификатора html в фактический символ, который он представляет.
Для 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.
Используйте решение daniel, если набор закодированных символов относительно ограничен. В противном случае используйте одну из многочисленных библиотек разбора HTML.
Мне нравится BeautifulSoup, потому что он может обрабатывать неверные XML/HTML:
http://www.crummy.com/software/BeautifulSoup/
для вашего вопроса, есть пример в их documentation
from BeautifulSoup import BeautifulStoneSoup
BeautifulStoneSoup("Sacré bleu!",
convertEntities=BeautifulStoneSoup.HTML_ENTITIES).contents[0]
# u'Sacr\xe9 bleu!'
from bs4 import BeautifulSoup
BeautifulSoup("Sacré bleu!").contents[0]
В Python 3.4 +:
import html
html.unescape(your_string)
В нижней части этой страницы на вики Python существует как минимум 2 варианта для "unescape" html.
Даниэль в качестве ответа:
"escaping происходит только в 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 1.5.5
In [1]: from django.utils.text import unescape_entities
In [2]: unescape_entities('<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" />')
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" />'
"��"
, Затем, после другого result.encode('utf-16', 'surrogatepass').decode('utf-16')
, я наконец получил оригинал обратно.
Если кто-то ищет простой способ сделать это с помощью шаблонов django, вы всегда можете использовать такие фильтры:
<html>
{{ node.description|safe }}
</html>
У меня были некоторые данные, поступающие от поставщика, и все, что у меня было, содержало html-теги, фактически написанные на отображаемой странице, как если бы вы смотрели на источник. Этот код мне очень помог. Надеюсь, это поможет другим.
Ура!!
Вы также можете использовать django.utils.html.escape
from django.utils.html import escape
something_nice = escape(request.POST['something_naughty'])
Я нашел это в исходном коде Cheetah (здесь)
htmlCodes = [
['&', '&'],
['<', '<'],
['>', '>'],
['"', '"'],
]
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 как список кортежей, а не список списков... это происходит в моей библиотеке, хотя:)
Я заметил, что ваш заголовок тоже запросил кодировку, поэтому здесь используется функция кодирования 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
Ища простейшее решение этого вопроса в Django и Python, я обнаружил, что вы можете использовать встроенные их функции для экранирования/отмены кода html.
Я сохранил ваш HTML-код в scraped_html
и clean_html
:
scraped_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" />'
)
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
Чтобы удалить свой очищенный HTML-код, вы можете использовать django.utils.text.unescape_entities, который:
Преобразуйте все именованные и числовые ссылки на символы в соответствующие символы Юникода.
>>> 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, который:
Преобразуйте все именованные и числовые ссылки на символы (например,
>
&x3e;
>
&x3e;
) в строке s в соответствующие символы Unicode.
>>> from html import unescape
>>> clean_html == unescape(scraped_html)
True
Чтобы избежать вашего чистого HTML-кода, вы можете использовать html.escape, который:
Преобразуйте символы
&
,<
и>
в строке s в безопасные для HTML последовательности.
>>> from html import escape
>>> scraped_html == escape(clean_html)
True
Это самое легкое решение этой проблемы -
{% autoescape on %}
{{ body }}
{% endautoescape %}
От эта страница.
Ниже приведена функция python, которая использует модуль htmlentitydefs
. Это не идеально. Версия htmlentitydefs
, которая у меня есть, является неполной и предполагает, что все сущности декодируются на один код, что неверно для таких объектов, как ≂̸
:
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) # ' ' -> unichr(10)
if hex_digits: return unichr(int(hex_digits, 16))
decimal_digits = match.group(2) # '' -> unichr(0x10)
if decimal_digits: return unichr(int(decimal_digits, 10))
name = match.group(3) # name is 'lt' when '<' was matched.
if name:
decoding = (htmlentitydefs.name2codepoint.get(name)
# Treat > like >.
# This is wrong for ≫ and ≪ 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;"