Python / Django kullanarak HTML kod çözme / kodlamayı nasıl gerçekleştiririm?


128

HTML kodlu bir dizem var:

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

Bunu şu şekilde değiştirmek istiyorum:

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

Metin olarak görüntülenmek yerine tarayıcı tarafından bir resim olarak işlenmesi için bunun HTML olarak kaydedilmesini istiyorum.

Dize bu şekilde depolanır, çünkü ben adında bir web-kazıma aracı kullanıyorum BeautifulSoup, bir web sayfasını "tarar" ve ondan belirli içeriği alır, ardından dizeyi o biçimde döndürür.

Bunu C # 'da nasıl yapacağımı buldum ama Python'da bulamadım . Biri bana yardım edebilir mi?

İlişkili

Yanıtlar:


118

Django kullanım durumu göz önüne alındığında, bunun iki cevabı var. İşte django.utils.html.escapereferans için işlevi:

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

Bunu tersine çevirmek için, Jake'in cevabında açıklanan Cheetah işlevi çalışmalıdır, ancak tek alıntı eksiktir. Bu sürüm, simetrik sorunları önlemek için değiştirilme sırasının tersine çevrildiği güncellenmiş bir demet içerir:

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)

Ancak bu genel bir çözüm değildir; yalnızca ile kodlanmış dizeler için uygundur django.utils.html.escape. Daha genel olarak, standart kitaplığa bağlı kalmak iyi bir fikirdir:

# 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)

Bir öneri olarak: HTML'nin çıkış karaktersiz olarak veritabanınızda saklanması daha mantıklı olabilir. Mümkünse BeautifulSoup'tan kaçılamayan sonuçları geri almak ve bu süreçten tamamen kaçınmak faydalı olacaktır.

Django ile, kaçış yalnızca şablon oluşturma sırasında gerçekleşir; bu yüzden kaçmayı önlemek için şablon motoruna dizinizden kaçmamasını söyleyin. Bunu yapmak için şablonunuzda şu seçeneklerden birini kullanın:

{{ context_var|safe }}
{% autoescape off %}
    {{ context_var }}
{% endautoescape %}

1
Neden Django veya Cheetah kullanmıyorsunuz?
Mat

4
Django.utils.html.escape'in tersi yok mu?
Mat

12
Sanırım kaçış yalnızca Django'da şablon oluşturma sırasında gerçekleşir. Bu nedenle, ortaya çıkmaya gerek yoktur - sadece şablon motoruna kaçmamasını söylersiniz. {{context_var | safe}} veya {% autoescape off%} {{context_var}} {% endautoescape%}
Daniel Naab

3
@Daniel: Lütfen yorumunuzu bir cevapla değiştirin ki ben de oylayabileyim! | güvenli tam olarak benim (ve eminim diğerlerinin) bu soruya cevap olarak aradığı şeydi.
Wayne Koorts

1
html.parser.HTMLParser().unescape()3.5'te kullanımdan kaldırılmıştır. html.unescape()Bunun yerine kullanın .
pjvandehaar

114

Standart kitaplıkla:

  • 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("&gt;"))

12
Bence bu en basit, 'pil dahil' ve doğru cevap. İnsanların bu Django / Cheetah olayına neden oy verdiklerini bilmiyorum.
Daniel Baktiar

Ben de öyle düşünüyorum, ancak bu cevabın tam görünmemesi dışında. buradaHTMLParser görüldüğü gibi, alt sınıflara ayrılması, beslendiği herhangi bir nesnenin tüm parçalarıyla ne yapılacağının söylenmesi ve ardından ayrıştırılacak nesnenin beslenmesi gerekir . Ayrıca, her html kimliğini temsil ettiği gerçek karaktere dönüştürmek için yine de dict'i kullanmak isteyeceksiniz . name2codepoint
Marconius

Haklısın. HTMLParserİçine bir HTML varlığı koyarsak , alt sınıfı olmayanlar istediğimiz gibi çalışamazdı. Belki adlandırmak gerekir htmlparseriçin _htmlparsergizlemek amacıyla ve sadece maruz unescapesadece yardımcı fonksiyonu gibi olmak yöntemini.
Jiangge Zhang

3
2015 yılı için bir not olan HTMLParser.unescape py 3.4'te kaldırılmış ve 3.5'te kaldırılmıştır. kullanmak from html import unescapeyerine
Karolis Ryselis

2
Bunun Alman Umlautları ("Ü")
576i

80

Html kodlaması için, standart kitaplıkta cgi.escape var :

>> 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 kod çözme için aşağıdakileri kullanıyorum:

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)

Daha karmaşık bir şey için BeautifulSoup kullanıyorum.


20

Kodlanmış karakterler kümesi görece sınırlıysa, daniel'ın çözümünü kullanın. Aksi takdirde, çok sayıda HTML ayrıştırma kitaplığından birini kullanın.

BeautifulSoup'u seviyorum çünkü hatalı biçimlendirilmiş XML / HTML'yi işleyebiliyor:

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

sorunuz için belgelerinde bir örnek var

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

BeautifulSoup onaltılık varlıkları (& # x65;) dönüştürmez stackoverflow.com/questions/57708/…
jfs

1
BeautifulSoup4 için eşdeğeri şöyle olacaktır:from bs4 import BeautifulSoup BeautifulSoup("Sacr&eacute; bl&#101;u!").contents[0]
radicand



6

Daniel'in bir cevap olarak yorumu:

"kaçış yalnızca Django'da şablon oluşturma sırasında gerçekleşir. Bu nedenle, bir çıkış yapılmasına gerek yoktur - sadece şablon oluşturma motoruna kaçmamasını söylersiniz. {{context_var | safe}} veya {% autoescape off%} {{context_var}} { % endautoescape%} "


Benim Django sürümümde 'güvenli' olmaması dışında çalışır. Onun yerine 'kaçış' kullanıyorum. Sanırım aynı şey.
willem

1
@willem: Tam tersi!
Aşera

5

İyi bir işlev buldum: 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]

Yeniden kullanmanın yararı, hem & # 039; ve & # 39; aynı aramayı kullanarak.
Neal Stublen

Bu işlemez &#xA0;olarak aynı kapıya deşifre hangi &#160;ve &nbsp;.
Mike Samuel

3

Django şablonları aracılığıyla bunu yapmanın basit bir yolunu arayan biri varsa, her zaman aşağıdaki gibi filtreleri kullanabilirsiniz:

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

Bir satıcıdan bazı veriler aldım ve yayınladığım her şeyde, sanki kaynağa bakıyormuşsunuz gibi işlenmiş sayfada yazılmış html etiketleri vardı. Yukarıdaki kod bana çok yardımcı oldu. Umarım bu başkalarına yardımcı olur.

Alkış !!


3

Bu gerçekten eski bir soru olsa da işe yarayabilir.

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
Bu, html varlıkları olarak kodlanmış vekil çiftlerin kodunu çözebilen tek kişiydi "&#55349;&#56996;". Sonra birbiri ardına result.encode('utf-16', 'surrogatepass').decode('utf-16'), nihayet orijinali geri aldım.
rescdsk

1

Bunu Cheetah kaynak kodunda buldum ( burada )

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

Neden listeyi tersine çevirdiklerinden emin değilim, bunun kodlama yöntemleriyle ilgisi olduğunu düşünüyorum, bu yüzden sizin için tersine çevrilmesi gerekmeyebilir. Ayrıca senin yerinde olsaydım htmlCodes'u bir liste listesi yerine tuple listesi olacak şekilde değiştirirdim ... bu yine de kitaplığıma gidiyor :)

Başlığınızın da kodlanmasını istediğini fark ettim, işte Cheetah'ın kodlama işlevi.

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
Liste tersine çevrilir çünkü kod çözme ve kodlama değişimlerinin her zaman simetrik olarak yapılması gerekir. Ters çevirmeden örneğin yapabilirsiniz. dönüştür '& amp; lt;' '& lt;', ardından sonraki adımda yanlış bir şekilde bunu '<' olarak dönüştürün.
bobince

1

Django.utils.html.escape'i de kullanabilirsiniz.

from django.utils.html import escape

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

OP, kaçmayı değil, kaçmayı sordu.
claymation

Başlıkta o da kodlama istedi - cevabınızı yeni buldu ve bunun için minnettarım.
Simon Steinberger

1
OP'nin sorduğu şey değil, ama bunu yararlı buldum.
dikdörtgen

0

Aşağıda modül kullanan bir python işlevi bulunmaktadır htmlentitydefs. Mükemmel değil. htmlentitydefsSahip olduğum sürüm eksiktir ve tüm varlıkların tek bir kod noktasına çözüldüğünü varsayar, bu da aşağıdaki gibi varlıklar için yanlıştır &NotEqualTilde;:

http://www.w3.org/TR/html5/named-character-references.html

NotEqualTilde;     U+02242 U+00338    ≂̸

Bu uyarılarla birlikte, işte kod.

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

Bu, bu problem için en kolay çözüm -

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

Gönderen bu sayfada .


0

Django ve Python'da bu sorunun en basit çözümünü araştırırken, html kodundan kaçış / unescape için kendi işlevlerini kullanabileceğinizi öğrendim.

Misal

HTML kodunuzu şuraya kaydettim scraped_htmlve 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

Django> = 1.0'a ihtiyacınız var

çıkış yapılmış

Kazınan html kodunuzu eski haline getirmek için django.utils.text.unescape_entities'i kullanabilirsiniz :

Tüm adlandırılmış ve sayısal karakter referanslarını karşılık gelen unicode karakterlerine dönüştürün.

>>> from django.utils.text import unescape_entities
>>> clean_html == unescape_entities(scraped_html)
True

kaçış

Senin temiz html kodunu kaçmak için kullanabilirsiniz django.utils.html.escape hangi:

Verilen metni HTML'de kullanılmak üzere kodlanmış ve işaretleriyle, tırnak işaretleriyle ve açılı ayraçlarla döndürür.

>>> from django.utils.html import escape
>>> scraped_html == escape(clean_html)
True

piton

Python> = 3.4'e ihtiyacınız var

çıkış yapılmış

Kazınan html kodunuzu eski haline getirmek için html.unescape'i kullanabilirsiniz.

Hepsinin adı ve sayısal karakter başvuruları (örneğin dönüştürme &gt;, &#62;, &x3e;karşılık gelen unicode karakter dizesi s).

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

kaçış

Senin temiz html kodunu kaçmak için kullanabilirsiniz html.escape hangi:

Karakterleri dönüştürme &, <ve >HTML güvenli dizilere dize s.

>>> from html import escape
>>> scraped_html == escape(clean_html)
True
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.