Python / Djangoを使用してHTMLデコード/エンコードを実行するにはどうすればよいですか?


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として登録して、テキストとして表示されるのではなく、ブラウザによって画像としてレンダリングされるようにしたいと思います。

文字列はそのように保存されています。私はというWebスクレイピングツールを使用しているためBeautifulSoup、Webページを「スキャン」して特定のコンテンツを取得し、その形式で文字列を返します。

C#でこれを行う方法を見つけましたが、Pythonではできません。誰かが私を助けてくれますか?

関連した

回答:


118

Djangoのユースケースを考えると、これには2つの答えがあります。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 %}

1
DjangoやCheetahを使ってみませんか?
マット

4
django.utils.html.escapeの反対はありませんか?
マット

12
テンプレートのレンダリング中にDjangoでのみエスケープが発生すると思います。したがって、アンエスケープの必要はありません。テンプレートエンジンにエスケープしないように指示するだけです。{{context_var | safe}}または{%autoescape off%} {{context_var}} {%endautoescape%}
Daniel Naab 2009

3
@ダニエル:私が投票できるように、コメントを回答に変更してください!|安全は、まさに私(そして他の人も)がこの質問に答えるために探していたものでした。
Wayne Koorts 09年

1
html.parser.HTMLParser().unescape()3.5では非推奨です。html.unescape()代わりに使用してください。
pjvandehaar 2015

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
これは最も簡単な「バッテリー込み」の正解だと思います。なぜ人々がDjango / Cheetahのものに投票するのか私にはわかりません。
Daniel Baktiar

私もそう思いますが、この答えは完全ではないようです。HTMLParserここに示すように、サブクラス化し、フィードされるオブジェクトのすべての部分をどう処理するかを指示し、解析するオブジェクトをフィードする必要があります。また、name2codepointdict を使用して、各html IDを、それが表す実際の文字に変換することもできます。
Marconius

あなたが正しい。サブクラスHTMLParser化されていないものは、HTMLエンティティを挿入した場合、期待どおりに機能しませんでした。たぶん、名前htmlparserを変更し_htmlparserて非表示にし、unescapeメソッドをヘルパー関数のように公開するだけです。
ジャンゲ・チャン

3
2015年の注記、HTMLParser.unescapeはpy 3.4で非推奨になり、3.5で削除されました。from html import unescape代わりに使用
Karolis Ryselis 2015

2
これはドイツのウムラウト( "(")のような特殊文字を処理しないことに注意してください
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解析ライブラリのいずれかを使用します。

不正なXML / HTMLを処理できるので、BeautifulSoupが好きです。

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%}」


動作しますが、私のバージョンのDjangoには「安全」機能がありません。代わりに「エスケープ」を使用します。同じことだと思います。
ウィレム2009

1
@willem:反対です!
Asherah 2015年

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;
Mike Samuel

3

djangoテンプレートを使用してこれを行う簡単な方法を誰かが探している場合は、次のようなフィルターをいつでも使用できます。

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

私はベンダーからのデータをいくつか持っていて、私が投稿したすべてのものには、ソースを見ているかのように、レンダリングされたページに実際に記述されたhtmlタグがありました。上記のコードは私を大いに助けました。これが他の人を助けることを願っています。

乾杯!!


3

これは本当に古い質問ですが、うまくいくかもしれません。

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

1
これは、のようなHTMLエンティティとしてエンコードされたサロゲートペアをデコードできた唯一のものでした"&#55349;&#56996;"。それから次々とresult.encode('utf-16', 'surrogatepass').decode('utf-16')、やっとオリジナルのバックが手に入りました。
rescdsk 16

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;'に変換し、次のステップでそれを誤って '<'に変換します。
ボビンス2008年

1

django.utils.html.escapeを使用することもできます

from django.utils.html import escape

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

OPは、脱出ではなく脱出について質問しました。
クレイメーション

タイトルitellfで彼はエンコーディングも要求しました-ちょうどあなたの答えを見つけ、それを感謝しています。
Simon Steinberger、2012

1
OPが要求したものではありませんが、これは便利だと思いました。
rectangletangle

0

以下は、moduleを使用するpython関数ですhtmlentitydefs。完璧ではありません。htmlentitydefs私が持っているバージョンは不完全であり、すべてのエンティティが1つのコードポイントにデコードされると想定しています&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でこの質問の最も簡単な解決策を検索すると、組み込みの関数を使用して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" />'
)

ジャンゴ

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> = 3.4が必要です

逃げる

スクレイピングしたhtmlコードのエスケープを解除するには、次のhtml.unescapeを使用できます。

すべての名前が付けられ、数値文字参照(例えば変換&gt;&#62;&x3e;対応するUnicode文字に文字列s中に)。

>>> 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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.