У меня есть строка в кодировке 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. Кто-нибудь может мне помочь?






Я нашел это в исходном коде 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, есть два ответа на этот вопрос. Вот его функция 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 %}
Почему бы не использовать Django или Cheetah?
Нет ли противоположности django.utils.html.escape?
Я думаю, что экранирование происходит только в Django во время рендеринга шаблона. Следовательно, нет необходимости в отмене экранирования - вы просто говорите движку шаблонов, чтобы он не сбегал. либо {{context_var | safe}}, либо {% autoescape off%} {{context_var}} {% endautoescape%}
@Daniel: Пожалуйста, измените свой комментарий на ответ, чтобы я мог проголосовать за него! | safe было именно тем, что я (и я уверен, что другие) искал, отвечая на этот вопрос.
Должно быть '& # 39;' вместо "& # 39; /".
Я обнаружил, что в django 1.3.x мне не удалось экранировать одинарные кавычки.
html.parser.HTMLParser().unescape() устарел в версии 3.5. Вместо этого используйте html.unescape().
Хороший вариант вернуть строку в предыдущее состояние. Спасибо
Используйте решение Дэниела, если набор закодированных символов относительно ограничен. В противном случае используйте одну из многочисленных библиотек анализа HTML.
Мне нравится BeautifulSoup, потому что он может обрабатывать искаженный XML / HTML:
http://www.crummy.com/software/BeautifulSoup/
на ваш вопрос есть пример в их документация
from BeautifulSoup import BeautifulStoneSoup
BeautifulStoneSoup("Sacré bleu!",
convertEntities=BeautifulStoneSoup.HTML_ENTITIES).contents[0]
# u'Sacr\xe9 bleu!'
BeautifulSoup не конвертирует шестнадцатеричные объекты (& # x65;) stackoverflow.com/questions/57708/…
Для BeautifulSoup4 эквивалент будет: from bs4 import BeautifulSoupBeautifulSoup("Sacré bleu!").contents[0]
См. В нижней части этого страница в Python wiki, есть как минимум 2 варианта "отмены экранирования" 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.
Комментарий Даниила в качестве ответа:
"экранирование происходит только в Django во время рендеринга шаблона. Следовательно, в этом нет необходимости - вы просто указываете движку шаблонов, чтобы он не экранировался. либо {{context_var | safe}}, либо {% autoescape off%} {{context_var}} { % endautoescape%} "
Работает, за исключением того, что в моей версии Django нет «безопасной». Вместо этого я использую «побег». Полагаю, это то же самое.
@willem: они наоборот!
Я нашел прекрасную функцию по адресу: 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; используя тот же поиск.
Это не обрабатывает  , который должен декодировать то же самое, что и   и .
Со стандартной библиотекой:
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(">"))
Я думаю, что это наиболее простой и правильный ответ «с батареей». Я не знаю, почему люди голосуют за эту штуку с Django / Cheetah.
Я тоже так думаю, за исключением того, что этот ответ не кажется полным. HTMLParser необходимо разделить на подклассы, указать, что делать со всеми частями любого объекта, который он загружает, а затем передать объект для анализа, как показано здесь. Кроме того, вы все равно захотите использовать dict name2codepoint для преобразования каждого идентификатора html в фактический символ, который он представляет.
Ты прав. Неклассифицированный HTMLParser не смог бы работать так, как мы хотели бы, если бы мы поместили в него HTML-сущность. Может быть, мне следует переименовать htmlparser в _htmlparser, чтобы скрыть его, и предоставить метод unescape только как вспомогательную функцию.
Примечание для 2015 года: HTMLParser.unescape устарел в py 3.4 и удален в 3.5. используйте from html import unescape вместо
@KarolisRyselis Ответ обновлен. Спасибо!
Обратите внимание, что это не обрабатывает специальные символы, такие как немецкие умляуты ("Ü").
@ 576i Не могли бы вы уточнить? Unescaping у меня работает с Python2 + 3.
Ниже представлена функция 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;"
Если кто-то ищет простой способ сделать это с помощью шаблонов django, вы всегда можете использовать такие фильтры:
<html>
{{ node.description|safe }}
</html>
У меня были данные от поставщика, и все, что я опубликовал, содержало html-теги, фактически написанные на отображаемой странице, как если бы вы смотрели на источник.
Вы также можете использовать django.utils.html.escape
from django.utils.html import escape
something_nice = escape(request.POST['something_naughty'])
ОП спрашивал о том, чтобы не сбежать.
В названии он тоже просил кодировку - просто нашел ваш ответ и благодарен за него.
Не то, о чем спрашивал ОП, но я нашел это полезным.
Это самое простое решение этой проблемы -
{% autoescape on %}
{{ body }}
{% endautoescape %}
От эта страница.
Хотя это действительно старый вопрос, он может сработать.
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" />'
Это был единственный способ декодировать суррогатные пары, закодированные как объекты HTML, например "��". Потом, после еще одного result.encode('utf-16', 'surrogatepass').decode('utf-16'), я наконец получил обратно оригинал.
В Python 3.4+:
import html
html.unescape(your_string)
Ты спас мне день. Я часами искал ответ. Я сохранил тексты с немецкими умляутами в файл, и мне пришлось как-то их конвертировать обратно. Отлично работает. import html html.unescape ('Klima & # 228; nderungen') 'Klimaänderungen'
В поисках простейшего решения этого вопроса в 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, который:
Convert all named and numeric character references to the corresponding unicode characters.
>>> from django.utils.text import unescape_entities
>>> clean_html == unescape_entities(scraped_html)
True
Чтобы избежать вашего чистого html-кода, вы можете использовать django.utils.html.escape, который:
Returns the given text with ampersands, quotes and angle brackets encoded for use in HTML.
>>> from django.utils.html import escape
>>> scraped_html == escape(clean_html)
True
Вам нужен Python> = 3.4
Чтобы отменить экранирование вашего очищенного html-кода, вы можете использовать html.unescape, который:
Convert all named and numeric character references (e.g.
>,>,&x3e;) in the string s to the corresponding unicode characters.
>>> from html import unescape
>>> clean_html == unescape(scraped_html)
True
Чтобы избежать вашего чистого html-кода, вы можете использовать html.escape, который:
Convert the characters
&,<and>in string s to HTML-safe sequences.
>>> from html import escape
>>> scraped_html == escape(clean_html)
True
Список перевернут, потому что замены декодирования и кодирования всегда должны выполняться симметрично. Без реверса вы могли бы, например. преобразовать '& amp; lt;' в '& lt;', затем на следующем шаге неправильно преобразовать это в '<'.