cgi.escape似乎是一种可行的选择。它的效果如何?有没有更好的选择?
html.escape
现在是正确的答案,Python 3.2之前使用的是cgi.escape
。它会对以下字符进行转义:
<
转为 <
>
转为 >
&
转为 &
这已经足够处理所有的HTML了。
编辑:如果您还需要转义非ASCII字符以便在另一种编码的文档中使用,就像Craig所说的那样,只需使用:
data.encode('ascii', 'xmlcharrefreplace')
不要忘记先解码data
为unicode
,使用相应的编码。
但是,根据我的经验,如果从一开始就始终使用unicode
,那么这种编码方式就没有用处。最好在末尾使用文档头中指定的编码进行编码(utf-8
以实现最大兼容性)。
例如:
>>> cgi.escape(u'<a>bá</a>').encode('ascii', 'xmlcharrefreplace')
'<a>bá</a>
值得注意的是(感谢Greg),cgi.escape
函数还有一个额外的quote
参数。当将其设置为True
时,cgi.escape
函数会转义双引号字符("
),这样你就可以在XML/HTML属性中使用结果值。
注意,Python 3.2中已经弃用了cgi.escape
函数,推荐使用html.escape
函数代替,两者功能类似,只不过quote
默认为True。
Python 3.2引入了一个新的html
模块,用于从HTML标记中转义保留字符。
它只有一个函数escape()
:
>>> import html
>>> html.escape('x > 2 && x < 7 single quote: \' double quote: "')
'x > 2 && x < 7 single quote: ' double quote: "'
quote=True
是什么意思? - 2rs2tshtml.escape()
会转义引号(相比之下,cgi.quote()
不会这样做,只有在被告知时才会转义双引号)。因此,我必须显式地设置一个可选参数来将某些内容注入到带属性的标签上,即使用html.escape()
使其对属性不安全: t = '" onclick="alert()'; t = html.escape(t, quote=False); s = f'<a href="about.html" class="{t}">foo</a>'
- maxschlepzigescape()
不足以使属性安全。换句话说,这样做并不安全:<a href=" {{ html.escape(untrusted_text) }} ">
- pianoJameshref
等属性中使用内联JavaScript的简单方法是设置禁止其执行的内容安全策略。 - maxschlepzightml.escape
会转义单引号和双引号。 - Flimm还有一个非常优秀的markupsafe包。
>>> from markupsafe import Markup, escape
>>> escape("<script>alert(document.cookie);</script>")
Markup(u'<script>alert(document.cookie);</script>')
markupsafe
软件包经过精心设计,可能是最通用和最符合Pythonic的转义方式,我个人认为,因为:
Markup
)是从unicode派生的类(即isinstance(escape('str'), unicode) == True
__html__
属性的对象)和模板重载(__html_format__
)。cgi.escape
可以用于在有限的意义上转义HTML标签和字符实体。
但是,您可能还需要考虑编码问题:如果要引用的HTML在特定编码中具有非ASCII字符,则还必须确保在引用时以明智的方式表示这些字符。也许您可以将它们转换为实体。否则,您应该确保在“源”HTML和嵌入页面之间进行正确的编码转换,以避免损坏非ASCII字符。
无需使用任何库,纯Python实现,可以将文本安全地转换为HTML文本:
text.replace('&', '&').replace('>', '>').replace('<', '<'
).replace('\'',''').replace('"','"').encode('ascii', 'xmlcharrefreplace')
<
将被转义为 &lt;
。 - Jason S虽然不是最简单的方法,但仍然很直接。与cgi.escape模块的主要区别在于,如果您的文本中已经有&
,它仍将正常工作。从对它的评论中可以看出:
cgi.escape版本
def 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.'''
s = s.replace("&", "&") # Must be done first!
s = s.replace("<", "<")
s = s.replace(">", ">")
if quote:
s = s.replace('"', """)
return s
正则表达式版本
QUOTE_PATTERN = r"""([&<>"'])(?!(amp|lt|gt|quot|#39);)"""
def escape(word):
"""
Replaces special characters <>&"' to HTML-safe sequences.
With attention to already escaped characters.
"""
replace_with = {
'<': '>',
'>': '<',
'&': '&',
'"': '"', # should be escaped in attributes
"'": ''' # should be escaped in attributes
}
quote_pattern = re.compile(QUOTE_PATTERN)
return re.sub(quote_pattern, lambda x: replace_with[x.group(0)], word)
cgi.escape
扩展版这个版本改进了 cgi.escape
。它还保留了空格和换行符。返回一个 unicode
字符串。
def escape_html(text):
"""escape strings for display in HTML"""
return cgi.escape(text, quote=True).\
replace(u'\n', u'<br />').\
replace(u'\t', u' ').\
replace(u' ', u' ')
>>> escape_html('<foo>\nfoo\t"bar"')
u'<foo><br />foo "bar"'
对于Python 2.7中的旧代码,可以通过BeautifulSoup4实现:
>>> bs4.dammit import EntitySubstitution
>>> esub = EntitySubstitution()
>>> esub.substitute_html("r&d")
'r&d'
yourunicode
进行解码,但是你声称它已经是 Unicode 编码,所以它应该是 已经被解码 的。这将会调用 隐式 ASCII 编码 或者直接失败,具体取决于 Python 版本。如果你只是使用答案末尾提供的示例cgi.escape(u'Ω').encode('ascii', 'xmlcharrefreplace')
,那么它就可以正常工作了。所以我猜如果你遇到了错误,那么yourunicode
可能并不是真正的 Unicode 编码,你需要先对它进行解码才能得到 Unicode 编码。 - nosklotext.decode('utf-8').encode('ascii', 'xmlcharrefreplace')
时,出现了错误信息 "UnicodeDecodeError: 'utf8' codec can't decode byte 0xfc in position 76: invalid start byte"。尝试使用decode('Unicode')
则会提示未知编码:Unicode。 - 576icgi.escape
在Python 3.8中已被移除。 - 0x5453