ฉันจะถอดรหัส / เข้ารหัส HTML โดยใช้ Python / Django ได้อย่างไร


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 เพื่อให้เบราว์เซอร์แสดงภาพเป็นรูปภาพแทนที่จะแสดงเป็นข้อความ

สตริงจะถูกจัดเก็บไว้เช่นนั้นเนื่องจากฉันใช้เครื่องมือขูดเว็บที่เรียกว่าBeautifulSoupมันจะ "สแกน" เว็บเพจและรับเนื้อหาบางอย่างจากนั้นส่งคืนสตริงในรูปแบบนั้น

ฉันได้พบวิธีการทำเช่นนี้ในC #แต่ไม่ได้อยู่ในหลาม มีคนช่วยฉันได้ไหม

ที่เกี่ยวข้อง

คำตอบ:


118

จากกรณีการใช้ 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 ควรใช้งานได้ แต่ไม่มีเครื่องหมายคำพูดเดี่ยว รุ่นนี้รวมถึง tuple ที่อัปเดตพร้อมกับคำสั่งของการเปลี่ยนกลับด้านเพื่อหลีกเลี่ยงปัญหาสมมาตร:

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 ที่ไม่ใช้ค่า Escape ในฐานข้อมูลของคุณอาจเหมาะสมกว่า มันจะคุ้มค่าที่จะได้รับผลลัพธ์ที่ไม่ใช้ค่า Escape จาก BeautifulSoup หากเป็นไปได้และหลีกเลี่ยงกระบวนการนี้โดยสิ้นเชิง

ด้วย Django การหลบหนีจะเกิดขึ้นเฉพาะในระหว่างการแสดงเทมเพลต ดังนั้นเพื่อป้องกันการหลบหนีคุณเพียงแค่บอกเครื่องยนต์ templating ที่จะไม่หนีสตริงของคุณ โดยใช้หนึ่งในตัวเลือกเหล่านี้ในเทมเพลตของคุณ:

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

1
ทำไมไม่ใช้ Django หรือ Cheetah?
Mat

4
django.utils.html.escape ตรงข้ามหรือไม่?
Mat

12
ฉันคิดว่าการหลบหนีเกิดขึ้นเฉพาะใน Django ระหว่างการแสดงเทมเพลต ดังนั้นจึงไม่จำเป็นต้องมียูเนสเคปคุณเพียงแค่บอกเครื่องมือสร้างเทมเพลตไม่ให้หนี อาจ {{context_var | safe}} หรือ {% autoescape off%} {{context_var}} {% endautoescape%}
Daniel Naab

3
@Daniel: โปรดเปลี่ยนความคิดเห็นของคุณเป็นคำตอบเพื่อให้ฉันสามารถโหวตได้! | safe เป็นสิ่งที่ฉัน (และฉันแน่ใจว่าคนอื่น) กำลังมองหาคำตอบสำหรับคำถามนี้
Wayne Koorts

1
html.parser.HTMLParser().unescape()ถูกคัดค้านใน 3.5 ใช้html.unescape()แทน
pjvandehaar

114

ด้วยห้องสมุดมาตรฐาน:

  • 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
ฉันคิดว่านี่เป็นคำที่ตรงไปตรงมาที่สุดคือ 'รวมแบตเตอรี่' และคำตอบที่ถูกต้อง ฉันไม่รู้ว่าทำไมผู้คนโหวตสิ่งเหล่านี้กับ Django / Cheetah
Daniel Baktiar

ฉันก็คิดเช่นกันยกเว้นว่าคำตอบนี้ดูเหมือนจะไม่สมบูรณ์ HTMLParserความต้องการที่จะ subclassed บอกว่าจะทำอย่างไรกับทุกส่วนของวัตถุใด ๆ ที่เป็นอาหารและเลี้ยงแล้ววัตถุที่จะแยกวิเคราะห์เท่าที่เห็นนี่ นอกจากนี้คุณยังคงต้องการใช้name2codepointdict เพื่อแปลงข้อมูลประจำตัว html ให้เป็นอักขระที่แท้จริง
Marconius

คุณถูก. unsubclassed HTMLParserไม่สามารถทำงานได้ตามที่เราต้องการหากเราใส่เอนทิตี HTML ลงไป บางทีฉันควรเปลี่ยนชื่อhtmlparserเป็น_htmlparserเพื่อซ่อนและเปิดเผยunescapeวิธีการให้เหมือนกับฟังก์ชันตัวช่วยเท่านั้น
Jiangge Zhang

3
หมายเหตุสำหรับปี 2558 HTMLParser.unescape ถูกคัดค้านใน py 3.4 และลบออกใน 3.5 ใช้from html import unescapeแทน
Karolis Ryselis

2
โปรดทราบว่านี่ไม่ได้จัดการอักขระพิเศษเช่น German Umlauts ("Ü")
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

ใช้วิธีแก้ปัญหาของ daniel หากชุดอักขระที่เข้ารหัสมีข้อ จำกัด มิฉะนั้นให้ใช้ไลบรารีการแยกวิเคราะห์ HTML จำนวนมาก

ฉันชอบ BeautifulSoup เพราะสามารถจัดการ XML / HTML ที่มีรูปแบบไม่ถูกต้อง:

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]
radicand



6

ความคิดเห็นของ Daniel เป็นคำตอบ:

"การหลบหนีเกิดขึ้นเฉพาะใน Django ในระหว่างการแสดงเทมเพลตดังนั้นจึงไม่จำเป็นต้องมี unescape - คุณเพียงแค่บอกเครื่องมือสร้างเทมเพลตไม่ให้หลบหนี {{context_var | safe}} หรือ {% autoescape off%} {{context_var}} {{ % endautoescape%} "


ใช้งานได้ยกเว้นรุ่น Django ของฉันไม่มี "ปลอดภัย" ฉันใช้ 'หลบหนี' แทน ฉันคิดว่ามันเป็นสิ่งเดียวกัน
วิลเล็ม

1
@willem: พวกเขาตรงกันข้าม!
Asherah

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]

ประโยชน์ของการใช้งานอีกครั้งคือคุณสามารถจับคู่ & # 039; และ & # 39; ใช้การค้นหาเดียวกัน
Neal Stublen

นี้ไม่ได้จัดการ&#xA0;ซึ่งควรที่จะถอดรหัสสิ่งเดียวและ&#160; &nbsp;
ไมค์ซามูเอล

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

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 เป็นรายการของ tuples แทนที่จะเป็นรายการ ... นี่จะเกิดขึ้นในห้องสมุดของฉัน :)

ฉันสังเกตเห็นชื่อของคุณขอการเข้ารหัสด้วยดังนั้นนี่คือฟังก์ชั่นการเข้ารหัสของเสือชีต้า

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;' จากนั้นในขั้นตอนถัดไปแปลงให้เป็น '<' ไม่ถูกต้อง
bobince

1

คุณยังสามารถใช้ django.utils.html.escape

from django.utils.html import escape

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

OP ถามเกี่ยวกับการหลบหลีกไม่หนี
claymation

ในชื่อของมันเขายังขอการเข้ารหัส - เพิ่งพบคำตอบของคุณและขอบคุณมัน
Simon Steinberger

1
ไม่ใช่สิ่งที่ OP ถาม แต่ฉันพบว่ามีประโยชน์
สี่เหลี่ยมผืนผ้า

0

htmlentitydefsด้านล่างนี้เป็นฟังก์ชั่นหลามที่ใช้โมดูล มันไม่สมบูรณ์แบบ รุ่นhtmlentitydefsที่ฉันมีไม่สมบูรณ์และสันนิษฐานว่าเอนทิตีทั้งหมดถอดรหัสหนึ่ง codepoint ซึ่งผิดสำหรับเอนทิตีเช่น&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 ฉันพบว่าคุณสามารถใช้ฟังก์ชัน builtin เพื่อหลบหนี / unescape html code

ตัวอย่าง

ฉันบันทึกรหัส 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

คุณต้องมี Django> = 1.0

unescape

หากต้องการยกเลิกการเข้ารหัสโค้ด 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

unescape

หากต้องการยกเลิกการเข้ารหัสโค้ด html ของคุณคุณสามารถใช้html.unescapeซึ่ง:

แปลงทุกชื่อและตัวเลขอ้างอิงตัว (เช่น&gt;, &#62;, &x3e;) ในสตริงกับอักขระ Unicode ที่สอดคล้องกัน

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