Làm cách nào để thực hiện giải mã / mã hóa HTML bằng Python / Django?


127

Tôi có một chuỗi được mã hóa 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" />'''

Tôi muốn thay đổi điều đó thành:

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

Tôi muốn đăng ký dưới dạng HTML để trình duyệt được hiển thị dưới dạng hình ảnh thay vì được hiển thị dưới dạng văn bản.

Chuỗi được lưu trữ như vậy bởi vì tôi đang sử dụng một công cụ BeautifulSoupquét web được gọi , nó "quét" một trang web và nhận được một số nội dung từ nó, sau đó trả về chuỗi theo định dạng đó.

Tôi đã tìm thấy cách thực hiện điều này trong C # nhưng không phải bằng Python . Ai đó có thể giúp tôi không?

Liên quan

Câu trả lời:


118

Với trường hợp sử dụng Django, có hai câu trả lời cho điều này. Đây là django.utils.html.escapechức năng của nó , để tham khảo:

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

Để đảo ngược điều này, hàm Cheetah được mô tả trong câu trả lời của Jake sẽ hoạt động, nhưng thiếu trích dẫn đơn. Phiên bản này bao gồm một bộ cập nhật, với thứ tự thay thế được đảo ngược để tránh các vấn đề đối xứng:

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)

Tuy nhiên, đây không phải là một giải pháp chung; nó chỉ thích hợp cho các chuỗi được mã hóa với django.utils.html.escape. Tổng quát hơn, đó là một ý tưởng tốt để gắn bó với thư viện tiêu chuẩn:

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

Như một gợi ý: có thể có ý nghĩa hơn khi lưu trữ HTML không thoát khỏi cơ sở dữ liệu của bạn. Sẽ đáng để xem xét nhận lại kết quả không được giải quyết từ BeautifulSoup nếu có thể và tránh hoàn toàn quá trình này.

Với Django, thoát chỉ xảy ra trong khi kết xuất mẫu; Vì vậy, để tránh thoát bạn chỉ cần nói với công cụ tạo khuôn mẫu không thoát khỏi chuỗi của bạn. Để làm điều đó, sử dụng một trong các tùy chọn sau trong mẫu của bạn:

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

1
Tại sao không sử dụng Django hoặc Cheetah?
Mat

4
Không có đối diện với django.utils.html.escape?
Mat

12
Tôi nghĩ thoát chỉ xảy ra trong Django trong quá trình kết xuất mẫu. Do đó, không cần phải có một cảnh quay - bạn chỉ cần nói với công cụ tạo khuôn mẫu không thoát ra. hoặc {{context_var | an toàn}} hoặc {% autoescape tắt%} {{context_var}} {% endautoescape%}
Daniel Naab

3
@Daniel: Vui lòng thay đổi nhận xét của bạn thành câu trả lời để tôi có thể bình chọn! | an toàn là chính xác những gì tôi (và tôi chắc chắn những người khác) đang tìm kiếm để trả lời cho câu hỏi này.
Wayne Koorts

1
html.parser.HTMLParser().unescape()bị phản đối trong 3,5. Sử dụng html.unescape()thay thế.
pjvandehaar

114

Với thư viện chuẩn:

  • Thoát 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
Tôi nghĩ rằng đây là cách đơn giản nhất, 'bao gồm pin' và câu trả lời đúng. Tôi không biết tại sao mọi người bầu chọn những thứ Django / Cheetah.
Daniel Baktiar

Tôi cũng nghĩ vậy, ngoại trừ câu trả lời này dường như không đầy đủ. HTMLParsercần phải được phân lớp, cho biết phải làm gì với tất cả các phần của bất kỳ đối tượng nào nó được cho ăn, và sau đó cho đối tượng được phân tích cú pháp, như được thấy ở đây . Ngoài ra, bạn vẫn sẽ muốn sử dụng name2codepointdict để chuyển đổi từng danh tính html thành char thực tế mà nó đại diện.
Marconius

Bạn đúng. Không được phân loại HTMLParserkhông thể hoạt động như chúng ta mong muốn nếu chúng ta đặt một thực thể HTML vào nó. Có lẽ tôi nên đổi tên htmlparserthành _htmlparserđể ẩn nó và chỉ hiển thị unescapephương thức giống như một hàm trợ giúp.
Jiangge Zhang

3
Một lưu ý cho năm 2015, HTMLParser.unescape không được dùng trong py 3.4 và bị xóa trong 3.5. sử dụng from html import unescapethay thế
Karolis Ryselis

2
Lưu ý rằng điều này không xử lý các ký tự đặc biệt như Umlauts của Đức ("")
576i

80

Đối với mã hóa html, có cgi.escape từ thư viện chuẩn:

>> 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.

Để giải mã html, tôi sử dụng như sau:

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)

Đối với bất cứ điều gì phức tạp hơn, tôi sử dụng BeautifulSoup.


20

Sử dụng giải pháp của daniel nếu tập hợp các ký tự được mã hóa tương đối hạn chế. Mặt khác, sử dụng một trong nhiều thư viện phân tích cú pháp HTML.

Tôi thích BeautifulSoup vì nó có thể xử lý XML / HTML không đúng định dạng:

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

cho câu hỏi của bạn, có một ví dụ trong tài liệu của họ

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

BeautifulSoup không chuyển đổi các đơn vị hex (& # x65;) stackoverflow.com/questions/57708/...
JFS

1
Đối với BeautifulSoup4, tương đương sẽ là:from bs4 import BeautifulSoup BeautifulSoup("Sacr&eacute; bl&#101;u!").contents[0]
radicand



6

Nhận xét của Daniel như một câu trả lời:

"thoát chỉ xảy ra trong Django trong quá trình kết xuất mẫu. Do đó, không cần phải có một cảnh quay - bạn chỉ cần nói với công cụ tạo khuôn mẫu không thoát. Hoặc {{bối cảnh | an toàn}} hoặc {% autoescape tắt%} {{bối cảnh_var}} { % endautoescape%} "


Hoạt động, ngoại trừ phiên bản Django của tôi không có 'an toàn'. Tôi sử dụng 'thoát' thay thế. Tôi cho rằng đó là điều tương tự.
willem

1
@willem: họ ngược lại!
Asherah

5

Tôi đã tìm thấy một chức năng tốt tại: 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]

Lợi ích của việc sử dụng lại là bạn có thể phù hợp với cả & # 039; và & # 39; sử dụng cùng một tìm kiếm.
Neal Stublen

Điều này không xử lý &#xA0;mà nên giải mã cho cùng một điều &#160;&nbsp;.
Mike Samuel

3

Nếu bất cứ ai đang tìm kiếm một cách đơn giản để làm điều này thông qua các mẫu django, bạn luôn có thể sử dụng các bộ lọc như thế này:

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

Tôi đã có một số dữ liệu đến từ một nhà cung cấp và mọi thứ tôi đăng đều có thẻ html thực sự được viết trên trang kết xuất như thể bạn đang xem nguồn. Các mã trên đã giúp tôi rất nhiều. Hy vọng điều này sẽ giúp những người khác.

Chúc mừng !!


3

Mặc dù đây là một câu hỏi thực sự cũ, nhưng điều này có thể làm việc.

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
Đây là người duy nhất có thể giải mã các cặp thay thế được mã hóa dưới dạng các thực thể html "&#55349;&#56996;". Sau đó result.encode('utf-16', 'surrogatepass').decode('utf-16'), cuối cùng, tôi đã có bản gốc trở lại.
rescdsk

1

Tôi tìm thấy điều này trong mã nguồn Cheetah ( ở đây )

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

không chắc chắn tại sao họ đảo ngược danh sách, tôi nghĩ nó phải làm theo cách họ mã hóa, vì vậy với bạn có thể không cần phải đảo ngược. Ngoài ra nếu tôi là bạn, tôi sẽ thay đổi htmlCodes thành một danh sách các bộ dữ liệu thay vì danh sách các danh sách ... điều này sẽ xuất hiện trong thư viện của tôi :)

Tôi nhận thấy tiêu đề của bạn cũng được yêu cầu mã hóa, vì vậy đây là chức năng mã hóa của 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

2
Danh sách bị đảo ngược vì giải mã và thay thế mã hóa luôn phải được thực hiện đối xứng. Nếu không có sự đảo ngược, bạn có thể ví dụ. chuyển đổi '& amp; lt;' thành '& lt;', sau đó trong bước tiếp theo chuyển đổi không chính xác thành '<'.
bobince

1

Bạn cũng có thể sử dụng django.utils.html.escape

from django.utils.html import escape

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

OP hỏi về việc không chú ý, không trốn thoát.
đất sét

Trong tiêu đề, nó cũng yêu cầu mã hóa - chỉ cần tìm thấy câu trả lời của bạn và rất biết ơn về điều đó.
Simon Steinberger

1
Không phải những gì OP yêu cầu, nhưng tôi thấy điều này hữu ích.
hình chữ nhật

0

Dưới đây là một chức năng python sử dụng mô-đun htmlentitydefs. Nó không hoàn hảo. Phiên bản htmlentitydefsmà tôi có không đầy đủ và nó giả định rằng tất cả các thực thể giải mã thành một mật mã sai đối với các thực thể như &NotEqualTilde;:

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

NotEqualTilde;     U+02242 U+00338    ≂̸

Với những cảnh báo đó, đây là mã.

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

Đây là giải pháp dễ nhất cho vấn đề này -

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

Từ trang này .


0

Tìm kiếm giải pháp đơn giản nhất cho câu hỏi này trong Django và Python Tôi thấy bạn có thể sử dụng các hàm dựng sẵn của chúng để thoát / bỏ mã html.

Thí dụ

Tôi đã lưu mã html của bạn trong 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

Bạn cần Django> = 1.0

không cảnh

Để hủy bỏ mã html bị loại bỏ của bạn, bạn có thể sử dụng django.utils.text.unescape_entities trong đó:

Chuyển đổi tất cả các tham chiếu ký tự tên và số sang các ký tự unicode tương ứng.

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

bỏ trốn

Để thoát mã html sạch của bạn, bạn có thể sử dụng django.utils.html.escape :

Trả về văn bản đã cho bằng ký hiệu, dấu ngoặc kép và dấu ngoặc nhọn được mã hóa để sử dụng trong HTML.

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

Con trăn

Bạn cần Python> = 3,4

không cảnh

Để hủy bỏ mã html bị loại bỏ của bạn, bạn có thể sử dụng html.unescape :

Chuyển đổi tất cả các tài liệu tham khảo được đặt tên và số ký tự (ví dụ &gt;, &#62;, &x3e;) trong chuỗi s cho các ký tự unicode tương ứng.

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

bỏ trốn

Để thoát mã html sạch của bạn, bạn có thể sử dụng html.escape :

Chuyển đổi các nhân vật &, <>trong chuỗi s đến chuỗi HTML-an toàn.

>>> from html import escape
>>> scraped_html == escape(clean_html)
True
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.