在Python字符串中解码HTML实体?

374

我正在使用Beautiful Soup 3解析一些HTML,但它包含了HTML实体,而Beautiful Soup 3没有自动对其进行解码:

>>> from BeautifulSoup import BeautifulSoup

>>> soup = BeautifulSoup("<p>&pound;682m</p>")
>>> text = soup.find("p").string

>>> print text
&pound;682m

我该如何解码text中的HTML实体,以便得到"£682m"而不是"&pound;682m"


7个回答

714

Python 3.4+

使用html.unescape()函数:

import html
print(html.unescape('&pound;682m'))

提醒: html.parser.HTMLParser.unescape 已过时,并且应该在3.5中删除,尽管由于错误而被保留。它将很快从语言中删除。


Python 2.6-3.3

您可以使用标准库中的HTMLParser.unescape()

>>> try:
...     # Python 2.6-2.7 
...     from HTMLParser import HTMLParser
... except ImportError:
...     # Python 3
...     from html.parser import HTMLParser
... 
>>> h = HTMLParser()
>>> print(h.unescape('&pound;682m'))
£682m

您还可以使用six兼容性库来简化导入:

>>> from six.moves.html_parser import HTMLParser
>>> h = HTMLParser()
>>> print(h.unescape('&pound;682m'))
£682m

9
这种方法在Google应用引擎上似乎无法转义字符,比如"’",但在本地Python2.6上可以。至少它仍然可以解码实体,比如"""。 - gfxmonk
一个未记录的API如何被弃用?编辑了答案。 - Markus Unterwaditzer
@MarkusUnterwaditzer 没有理由不将未记录的方法弃用。这个方法会抛出弃用警告 - 请参见我对答案的编辑。 - Mark Amery
1
值得注意的是,对于Python 2:特殊字符将被替换为其Latin-1(ISO-8859-1)编码的对应字符。例如,可能需要使用h.unescape(s).encode("utf-8")。文档中提供了以下定义:“在Latin-1字符集(ISO-8859-1)中可以使用简单文本替换处理的所有XHTML 1.0定义的实体”。 - anonymous coward
1
为什么它不能处理“不要忘记π=3.14,而不是3”的情况? - canbax
显示剩余2条评论

72

Beautiful Soup可以处理实体转换。在Beautiful Soup 3中,您需要指定convertEntities参数给BeautifulSoup构造函数 (请参阅存档文档中的'实体转换'部分)。在Beautiful Soup 4中,实体会自动解码。

Beautiful Soup 3

>>> from BeautifulSoup import BeautifulSoup
>>> BeautifulSoup("<p>&pound;682m</p>", 
...               convertEntities=BeautifulSoup.HTML_ENTITIES)
<p>£682m</p>

美丽汤 4

>>> from bs4 import BeautifulSoup
>>> BeautifulSoup("<p>&pound;682m</p>")
<html><body><p>£682m</p></body></html>

+1。我不知道我在文档中是如何错过这个信息的:谢谢您提供的信息。不过我还是要接受luc的答案,因为他使用了我在问题中指定的标准库(对我来说不重要),并且它可能对其他人更有用。 - jkp
7
BeautifulSoup4 主要使用 HTMLParser。参见源代码 - scharfmn
4
在Beautiful Soup 4中,如何获取转换结果而不包含原始字符串中没有的所有冗余HTML标记?(例如<html>和<body>) - Praxiteles
@Praxiteles:BeautifulSoup('£682m', "html.parser") https://dev59.com/YmUq5IYBdhLWcg3wEcRZ#14822344 - Soitje

16
你可以使用w3lib.html库中的replace_entities函数。
In [202]: from w3lib.html import replace_entities

In [203]: replace_entities("&pound;682m")
Out[203]: u'\xa3682m'

In [204]: print replace_entities("&pound;682m")
£682m

7

Beautiful Soup 4可以让你设置输出格式

如果你传入formatter=None,Beautiful Soup在输出时不会对字符串进行任何修改。这是最快的选项,但可能会导致Beautiful Soup生成无效的HTML/XML,例如:

print(soup.prettify(formatter=None))
# <html>
#  <body>
#   <p>
#    Il a dit <<Sacré bleu!>>
#   </p>
#  </body>
# </html>

link_soup = BeautifulSoup('<a href="http://example.com/?foo=val1&bar=val2">A link</a>')
print(link_soup.a.encode(formatter=None))
# <a href="http://example.com/?foo=val1&bar=val2">A link</a>

这并没有回答问题。(另外,我也不知道文档中关于这里最后一部分HTML的无效内容是什么。) - Mark Amery
"<<Sacré bleu!>>"是无效的部分,因为它有未转义的<和>符号,会破坏周围的HTML。我知道这是我发的晚帖子,但如果有人正在寻找并想知道... - GMasucci

1

我遇到了类似的编码问题。我使用了normalize()方法。当我将数据框导出到另一个目录下的.html文件时,使用pandas.to_html()方法时出现Unicode错误。最终我采用了这种方法,它起作用了...

    import unicodedata 

数据框对象可以是你喜欢的任何对象,我们称之为table...
    table = pd.DataFrame(data,columns=['Name','Team','OVR / POT'])
    table.index+= 1

将表格数据进行编码,以便我们可以将其导出到模板文件夹中的 .html 文件中(这可以是您希望的任何位置 :))。
     #this is where the magic happens
     html_data=unicodedata.normalize('NFKD',table.to_html()).encode('ascii','ignore')

将标准化字符串导出到HTML文件
    file = open("templates/home.html","w") 

    file.write(html_data) 

    file.close() 

参考:unicodedata文档

这并没有回答问题。Unicode的规范化形式与HTML实体并不相同。 - undefined

0
import html
  
myHtml = "<body><h1> How to use html.unescape() in Python </h1></body>"
encodedHtml = html.escape(myHtml)
print("Encoded HTML: ", encodedHtml)
decodedHtml = html.unescape(encodedHtml)
  
print("Decoded HTML: ", decodedHtml)

-5

这可能与编程无关。但是,要从整个文档中消除这些HTML实体,您可以执行以下操作:(假设文档=页面,请原谅我的代码不够规范,但如果您有更好的想法,我非常乐意听取 - 我对此还很陌生)。

import re
import HTMLParser

regexp = "&.+?;" 
list_of_html = re.findall(regexp, page) #finds all html entites in page
for e in list_of_html:
    h = HTMLParser.HTMLParser()
    unescaped = h.unescape(e) #finds the unescaped value of the html entity
    page = page.replace(e, unescaped) #replaces html entity with unescaped value

10
不需要自己匹配HTML实体并循环,.unescape()已经为你做了这个。我不明白为什么你和Rob要发布这些过于复杂的解决方案,手动去匹配实体,而已经有一个被接受的答案清楚地展示了.unescape()可以在字符串中找到实体。 - Mark Amery

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接