2008-09-11 34 views
65

Tôi đang thực hiện một số trang web và các trang web thường xuyên sử dụng các thực thể HTML để biểu diễn các ký tự ascii không. Liệu Python có một tiện ích mà có một chuỗi với các thực thể HTML và trả về một kiểu unicode?Chuyển đổi các đối tượng XML/HTML thành chuỗi Unicode trong Python

Ví dụ:

tôi nhận được trở lại:

ǎ 

đại diện cho một "ǎ" với một dấu giai điệu. Trong dạng nhị phân, điều này được biểu diễn dưới dạng 16 bit 01 bit. Tôi muốn chuyển đổi đơn vị html vào giá trị u'\u01ce'

+0

liên quan: [Giải mã HTML thực thể trong Python chuỗi?] (Http://stackoverflow.com/q/2087370/4279) – jfs

Trả lời

57

Python có module htmlentitydefs, nhưng điều này không bao gồm một chức năng để tổ chức unescape HTML.

Python phát triển Fredrik Lundh (tác giả của ElementTree, trong số những thứ khác) có chức năng như vậy on his website, hoạt động với số thập phân, hex và các tổ chức có tên là:

import re, htmlentitydefs 

## 
# Removes HTML or XML character references and entities from a text string. 
# 
# @param text The HTML (or XML) source text. 
# @return The plain text, as a Unicode string, if necessary. 

def unescape(text): 
    def fixup(m): 
     text = m.group(0) 
     if text[:2] == "&#": 
      # character reference 
      try: 
       if text[:3] == "&#x": 
        return unichr(int(text[3:-1], 16)) 
       else: 
        return unichr(int(text[2:-1])) 
      except ValueError: 
       pass 
     else: 
      # named entity 
      try: 
       text = unichr(htmlentitydefs.name2codepoint[text[1:-1]]) 
      except KeyError: 
       pass 
     return text # leave as is 
    return re.sub("&#?\w+;", fixup, text) 
+3

Chức năng đó hoạt động tuyệt vời. Sống lâu Fredrik –

+0

Tuyệt đối. Tại sao không có trong stdlib? – smci

+0

Nhìn vào mã của nó, nó dường như không hoạt động với '&' và như vậy, phải không? – jnns

7

Bạn có thể tìm câu trả lời ở đây - Getting international characters from a web page?

EDIT: Nó có vẻ như BeautifulSoup không chuyển đổi các đơn vị được viết dưới dạng thập lục phân. Nó có thể được cố định:

import copy, re 
from BeautifulSoup import BeautifulSoup 

hexentityMassage = copy.copy(BeautifulSoup.MARKUP_MASSAGE) 
# replace hexadecimal character reference by decimal one 
hexentityMassage += [(re.compile('&#x([^;]+);'), 
        lambda m: '&#%d;' % int(m.group(1), 16))] 

def convert(html): 
    return BeautifulSoup(html, 
     convertEntities=BeautifulSoup.HTML_ENTITIES, 
     markupMassage=hexentityMassage).contents[0].string 

html = '<html>&#x01ce;&#462;</html>' 
print repr(convert(html)) 
# u'\u01ce\u01ce' 

EDIT:

unescape() chức năng được đề cập bởi @dF trong đó sử dụng htmlentitydefs mô-đun tiêu chuẩn và unichr() có thể thích hợp hơn trong trường hợp này.

+0

Giải pháp này không làm việc với các ví dụ: in BeautifulSoup (' & # x01ce ; ', convertEntities = BeautifulSoup.HTML_ENTITIES) Điều này trả về cùng một thực thể HTML – Cristian

+0

Tôi đã chỉnh sửa câu trả lời – jfs

+0

Lưu ý: điều này chỉ áp dụng cho BeautifulSoup 3, không được chấp nhận và được coi là cũ từ năm 2012. BeautifulSoup 4 xử lý các thực thể HTML như thế này một cách tự động . –

18

Sử dụng được xây dựng trong unichr - BeautifulSoup là không cần thiết:

>>> entity = '&#x01ce' 
>>> unichr(int(entity[3:],16)) 
u'\u01ce' 
+2

Nhưng điều đó đòi hỏi bạn phải tự động và rõ ràng biết nơi nào trong chuỗi ký tự Unicode được mã hóa là/đang - mà bạn không thể biết. Và bạn cần phải 'cố gắng ... bắt' ngoại lệ kết quả cho khi bạn làm sai. – smci

+0

'unichar' đã bị xóa trong python3. Bất kỳ đề xuất cho phiên bản đó? – Splatmistro

6

Đây là một chức năng mà sẽ giúp bạn để có được nó đúng và chuyển đổi các thực thể trở lại thành các ký tự utf-8.

def unescape(text): 
    """Removes HTML or XML character references 
     and entities from a text string. 
    @param text The HTML (or XML) source text. 
    @return The plain text, as a Unicode string, if necessary. 
    from Fredrik Lundh 
    2008-01-03: input only unicode characters string. 
    http://effbot.org/zone/re-sub.htm#unescape-html 
    """ 
    def fixup(m): 
     text = m.group(0) 
     if text[:2] == "&#": 
     # character reference 
     try: 
      if text[:3] == "&#x": 
       return unichr(int(text[3:-1], 16)) 
      else: 
       return unichr(int(text[2:-1])) 
     except ValueError: 
      print "Value Error" 
      pass 
     else: 
     # named entity 
     # reescape the reserved characters. 
     try: 
      if text[1:-1] == "amp": 
       text = "&amp;amp;" 
      elif text[1:-1] == "gt": 
       text = "&amp;gt;" 
      elif text[1:-1] == "lt": 
       text = "&amp;lt;" 
      else: 
       print text[1:-1] 
       text = unichr(htmlentitydefs.name2codepoint[text[1:-1]]) 
     except KeyError: 
      print "keyerror" 
      pass 
     return text # leave as is 
    return re.sub("&#?\w+;", fixup, text) 
+3

Tại sao câu trả lời này được sửa đổi? Nó có vẻ hữu ích với tôi. – dariopy

+1

vì người đó muốn ký tự trong unicode thay vì utf-8 ký tự. Tôi đoán :) – karlcow

3

Không chắc chắn tại sao luồng ngăn xếp ngăn xếp không bao gồm ';' trong tìm kiếm/thay thế (ví dụ: lambda m: '& #% d * ; *') Nếu không, BeautifulSoup có thể bị chặn bởi vì ký tự liền kề có thể được hiểu là một phần của mã HTML (ví dụ: & # 39B cho & # 39Blackout).

này làm việc tốt hơn cho tôi:

import re 
from BeautifulSoup import BeautifulSoup 

html_string='<a href="/cgi-bin/article.cgi?f=/c/a/2010/12/13/BA3V1GQ1CI.DTL"title="">&#x27;Blackout in a can; on some shelves despite ban</a>' 

hexentityMassage = [(re.compile('&#x([^;]+);'), 
lambda m: '&#%d;' % int(m.group(1), 16))] 

soup = BeautifulSoup(html_string, 
convertEntities=BeautifulSoup.HTML_ENTITIES, 
markupMassage=hexentityMassage) 
  1. Các int (m.group (1), 16) chuyển số (quy định tại cơ sở-16) định dạng lại một số nguyên.
  2. m.group (0) trả về toàn bộ trận đấu, m.group (1) trả về nhóm regexp chụp
  3. Về cơ bản sử dụng markupMessage là giống như:
    html_string = re.sub (' & #x ([ ^;] +); ', lambda m:' & #% d; '% int (m.nhóm (1), 16), html_string)
+0

cảm ơn vì đã phát hiện lỗi. Tôi đã chỉnh sửa [câu trả lời của tôi] (http://stackoverflow.com/questions/57708/convert-xml-html-entities-into-unicode-string-in-python/57745#57745). – jfs

16

Một thay thế, nếu bạn có lxml:

>>> import lxml.html 
>>> lxml.html.fromstring('&#x01ce').text 
u'\u01ce' 
+0

Hãy cẩn thận, vì điều này cũng có thể trả về một đối tượng kiểu 'str' nếu không có ký tự đặc biệt. – pintoch

+0

giải pháp tốt nhất khi mọi thứ thất bại, chỉ có lxml đến để giải cứu. :) –

48

HTMLParser rất riêng Các lib chuẩn của có chức năng unescape không có giấy tờ() mà thực hiện chính xác những gì bạn nghĩ nó:

import HTMLParser 
h = HTMLParser.HTMLParser() 
h.unescape('&copy; 2010') # u'\xa9 2010' 
h.unescape('&#169; 2010') # u'\xa9 2010' 
+0

[nó cũng hoạt động cho các thực thể hex] (http://ideone.com/u60Fz).[Thực hiện rất giống] (http://hg.python.org/cpython/file/60c831305e73/Lib/HTMLParser.py#l444) với hàm unescape() từ câu trả lời của [@ ​​dF.] (http://stackoverflow.com/a/58125/4279). – jfs

+8

Phương pháp này không được ghi trong tài liệu HTMLParser của Python và có một nhận xét trong nguồn cho biết nó được dùng để sử dụng nội bộ. Tuy nhiên, nó hoạt động giống như xử lý trong Python 2.6 đến 2.7, và có lẽ là giải pháp tốt nhất trên mạng. Trước phiên bản 2.6, nó sẽ chỉ giải mã các thực thể được đặt tên như '&' hoặc '> '. –

+5

Nó được hiển thị dưới dạng hàm 'html.unescape()' trong Python 3.4+ – jfs

9

Nếu bạn đang ở trên Python 3.4 hoặc mới hơn, bạn chỉ có thể sử dụng html.unescape:

s = html.unescape(s) 
1

Một giải pháp khác là thư viện dựng sẵn xml.sax.saxutils (cả cho html và xml). Tuy nhiên, nó sẽ chỉ chuyển đổi & gt, & amp và & lt.

from xml.sax.saxutils import unescape 

escaped_text = unescape(text_to_escape) 
0

Dưới đây là phiên bản Python 3 của dF's answer:

import re 
import html.entities 

def unescape(text): 
    """ 
    Removes HTML or XML character references and entities from a text string. 

    :param text: The HTML (or XML) source text. 
    :return:  The plain text, as a Unicode string, if necessary. 
    """ 
    def fixup(m): 
     text = m.group(0) 
     if text[:2] == "&#": 
      # character reference 
      try: 
       if text[:3] == "&#x": 
        return chr(int(text[3:-1], 16)) 
       else: 
        return chr(int(text[2:-1])) 
      except ValueError: 
       pass 
     else: 
      # named entity 
      try: 
       text = chr(html.entities.name2codepoint[text[1:-1]]) 
      except KeyError: 
       pass 
     return text # leave as is 
    return re.sub("&#?\w+;", fixup, text) 

Những thay đổi chính mối quan tâm htmlentitydefs mà bây giờ html.entitiesunichr mà bây giờ chr là. Xem này Python 3 porting guide.

+0

Trong Python 3, bạn chỉ cần sử dụng 'html.unescape()'; tại sao có một con chó và sủa mình? –

+0

Điểm tốt :) woof! – Victor

Các vấn đề liên quan