在Python中将XML / HTML实体转换为Unicode字符串

时间:2020-03-05 18:51:51  来源:igfitidea点击:

我正在做一些网页抓取工作,并且网站经常使用HTML实体来表示非ascii字符。 Python是否有一个实用程序可以接受带有HTML实体的字符串并返回unicode类型?

例如:

我回来了:

ǎ

代表带有色调标记的""。以二进制形式表示为16位01ce。我想将html实体转换为值'u'\ u01ce'

解决方案

回答

我们可以在这里找到答案-从网页上获取国际字符?

编辑:似乎" BeautifulSoup"不会转换以十六进制形式编写的实体。可以解决:

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'

编辑:

在这种情况下,@ dF提到的使用htmlentitydefs标准模块和unichr()unescape()函数可能更合适。

回答

使用内置的unichr-BeautifulSoup是不必要的:

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

回答

Python具有htmlentitydefs模块,但是其中不包含对HTML实体进行转义的功能。

Python开发人员Fredrik Lundh(elementtree的作者等)在他的网站上具有这样的功能,该功能可用于十进制,十六进制和命名实体:

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)

回答

此功能应该可以正确处理并将实体转换回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)

回答

不知道为什么堆栈溢出线程不包含';'在搜索/替换中(即lambda m:'&#%d *; *'),如果不这样做,BeautifulSoup可能会倒钩,因为相邻字符可以解释为HTML代码的一部分(即&#39B表示&# 39停电)。

这对我来说更好:

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)
  • int(m.group(1),16)将数字(以16为基数指定)格式转换回整数。
  • m.group(0)返回整个匹配项,m.group(1)返回正则表达式捕获组
  • 基本上使用markupMessage与以下内容相同:html_string = re.sub('&#x([^;] +);',lambda m:'&#%d;'%int(m.group(1),16) ,html_string)