【问题标题】:How do I perform HTML decoding/encoding using Python/Django?如何使用 Python/Django 执行 HTML 解码/编码?
【发布时间】:2010-09-21 11:16:35
【问题描述】:

我有一个 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 中却没有。有人可以帮帮我吗?

相关

【问题讨论】:

    标签: python django html-encode


    【解决方案1】:

    鉴于 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;'))
    

    要扭转这种情况,Jake 的回答中描述的 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 = (
                ("'", '&#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 %}
    

    【讨论】:

    • 为什么不使用 Django 或 Cheetah?
    • 没有django.utils.html.escape的对立面吗?
    • 我认为转义只发生在模板渲染期间的Django中。因此,没有必要进行转义 - 您只需告诉模板引擎不要转义。 {{ context_var|safe }} 或 {% autoescape off %}{{ context_var }}{% endautoescape %}
    • @Daniel:请将您的评论更改为答案,以便我投票! |safe 正是我(我相信其他人)在回答这个问题时所寻找的。​​span>
    • html.parser.HTMLParser().unescape() 在 3.5 中已弃用。请改用html.unescape()
    【解决方案2】:

    使用标准库:

    • HTML 转义

      try:
          from html import escape  # python 3.x
      except ImportError:
          from cgi import escape  # python 2.x
      
      print(escape("<"))
      
    • HTML 转义

      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;"))
      

    【讨论】:

    • 我认为这是最直接的,“包括电池”和正确的答案。我不知道为什么人们会投票给那些 Django/Cheetah 的东西。
    • 我也这么认为,只是这个答案似乎并不完整。 HTMLParser 需要被子类化,告诉如何处理它所馈送的任何对象的所有部分,然后馈送要解析的对象,如 here 所示。此外,您仍然需要使用 name2codepoint dict 将每个 html 标识转换为它所代表的实际字符。
    • 你是对的。如果我们将 HTML 实体放入其中,未分类的 HTMLParser 将无法按我们希望的方式工作。也许我应该将htmlparser 重命名为_htmlparser 以隐藏它,并且只公开unescape 方法就像一个辅助函数。
    • 2015 年的注释,HTMLParser.unescape 在 py 3.4 中已弃用并在 3.5 中删除。改用from html import unescape
    • 请注意,这不处理像德语变音符号(“Ü”)这样的特殊字符
    【解决方案3】:

    对于 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。

    【讨论】:

    • 来自 Python 文档:“自 3.2 版以来已弃用:此函数不安全,因为默认情况下 quote 为 false,因此已弃用。请改用 html.escape()。”从 3.9 和可能更早的版本开始,它已经消失了。
    【解决方案4】:

    如果编码字符集相对受限,请使用丹尼尔的解决方案。 否则,请使用众多 HTML 解析库之一。

    我喜欢 BeautifulSoup,因为它可以处理格式错误的 XML/HTML:

    http://www.crummy.com/software/BeautifulSoup/

    对于您的问题,他们的documentation 中有一个示例

    from BeautifulSoup import BeautifulStoneSoup
    BeautifulStoneSoup("Sacr&eacute; bl&#101;u!", 
                       convertEntities=BeautifulStoneSoup.HTML_ENTITIES).contents[0]
    # u'Sacr\xe9 bleu!'
    

    【讨论】:

    • BeautifulSoup 不转换十六进制实体 (e) stackoverflow.com/questions/57708/…
    • 对于 BeautifulSoup4,等价于:from bs4 import BeautifulSoupBeautifulSoup("Sacr&amp;eacute; bl&amp;#101;u!").contents[0]
    【解决方案5】:

    在 Python 3.4+ 中:

    import html
    
    html.unescape(your_string)
    

    【讨论】:

    • 你拯救了我的一天。我一直在寻找那个答案好几个小时。我将带有德语变音符号的文本保存到一个文件中,并且不得不以某种方式将它们转换回来。它就像一个魅力。 import html html.unescape('Klimaänderungen') 'Klimaänderungen'
    【解决方案6】:

    见底部page at Python wiki,至少有2个选项可以“取消转义”html。

    【讨论】:

      【解决方案7】:

      丹尼尔的评论作为答案:

      "转义只发生在 Django 中模板渲染期间。因此,不需要转义 - 你只需告诉模板引擎不要转义。{{ context_var|safe }} 或 {% autoescape off %}{{ context_var }}{% endautoescape %}"

      【讨论】:

      • 有效,只是我的 Django 版本没有“安全”。我改用“逃生”。我认为这是同一件事。
      • @willem:他们正好相反!
      【解决方案8】:

      我在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 的好处是可以同时匹配 '和 '使用相同的搜索。
      • 这不处理&amp;#xA0;,它应该解码为与&amp;#160;&amp;nbsp;相同的东西。
      【解决方案9】:

      如果有人正在寻找通过 django 模板执行此操作的简单方法,您可以随时使用如下过滤器:

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

      我有一些来自供应商的数据,我发布的所有内容都在呈现的页面上实际写入了 html 标签,就好像你在查看源代码一样。

      【讨论】:

        【解决方案10】:

        尽管这是一个非常老的问题,但它可能有效。

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

        【讨论】:

        • 这是唯一能够解码编码为 html 实体的代理对,例如 "&amp;#55349;&amp;#56996;"。然后又result.encode('utf-16', 'surrogatepass').decode('utf-16'),终于把原来的回来了。
        【解决方案11】:

        我在 Cheetah 源代码中找到了这个 (here)

        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
        

        【讨论】:

        • 这个列表是相反的,因为解码和编码替换总是必须对称地进行。如果没有倒车,你可以例如。转换 '&lt;' '<',然后在下一步错误地将其转换为'
        【解决方案12】:

        你也可以使用 django.utils.html.escape

        from django.utils.html import escape
        
        something_nice = escape(request.POST['something_naughty'])
        

        【讨论】:

        • OP 询问了取消转义,而不是转义。
        • 在标题中,他还要求编码 - 刚刚找到您的答案,非常感谢。
        • 不是 OP 要求的,但我发现这很有用。
        【解决方案13】:

        下面是一个使用模块htmlentitydefs的python函数。它并不完美。我拥有的htmlentitydefs 版本不完整,它假定所有实体都解码为一个代码点,这对于&amp;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;"
        

        【讨论】:

          【解决方案14】:

          这是解决这个问题最简单的方法 -

          {% autoescape on %}
             {{ body }}
          {% endautoescape %}
          

          来自this page

          【讨论】:

            【解决方案15】:

            在 Django 和 Python 中搜索此问题的最简单解决方案,我发现您可以使用他们的内置函数来转义/取消转义 html 代码。

            示例

            我将您的 html 代码保存在 scraped_htmlclean_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

            转义

            要取消您抓取的 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

            你需要 Python >= 3.4

            转义

            要取消您抓取的 html 代码,您可以使用 html.unescape 其中:

            将字符串 s 中的所有命名和数字字符引用(例如 &amp;gt;&amp;#62;&amp;x3e;)转换为相应的 unicode 字符。

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

            逃跑

            要转义干净的 html 代码,您可以使用 html.escape 其中:

            将字符串 s 中的字符 &amp;&lt;&gt; 转换为 HTML 安全序列。

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

            【讨论】:

              猜你喜欢
              • 2021-07-06
              • 2014-12-05
              • 2011-04-03
              • 2019-06-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-02-04
              相关资源
              最近更新 更多