使用python编辑html,但lxml将漂亮的html实体转换为奇怪的编码

时间:2011-02-02 16:00:12

标签: python character-encoding html-parsing lxml

我试图使用python(使用pyquery和lxml)来改变和清理一些HTML。

Eg. html = "<div><!-- word style><bleep><omgz 1,000 tags><--><p>It&#146;s a spicy meatball!</div>"

lxml.html.clean函数clean_html()运行良好,只不过它取代了很好的html实体,比如

&#146; 

带有一些unicode字符串

\xc2\x92

unicode在不同的浏览器中看起来很奇怪(使用自动编码的firefox和opera,utf8,latin-1等),就像一个空盒子。如何阻止lxml转换实体?如何以latin-1编码完成所有操作?看起来很奇怪,专门为html构建的模块会这样做。

我无法确定哪些角色存在,所以我无法使用

replace("\xc2\x92","&#146;").

我尝试过使用

clean_html(html).encode('latin-1')

但是unicode仍然存在。

是的,我告诉别人不要用文字来写html,但是我会听到整个

  

&#34;我喜欢它,因为你不能让我变得更好。

编辑:一个美丽的解决方案:

from BeautifulSoup import BeautifulSoup, Comment
soup = BeautifulSoup(str(desc[desc_type]))
                    comments = soup.findAll(text=lambda text:isinstance(text, Comment))
                    [comment.extract() for comment in comments]
                    print soup

3 个答案:

答案 0 :(得分:11)

有一些事情 - 如果你了解它们 - 将导致最简单/最好的解决方案:

  • clean_html()返回与您提供的相同类型:如果您给它一个字符串,它将返回一个字符串,但如果您给它一个Element或ElementTree,它将返回一个Element或ElementTree分别

  • 您可以通过为lxml.html.tostring()方法或树的write()方法提供编码选项来控制Element或ElementTree的序列化方式(顺便说一下,xml也是如此)。例如,您可以使用encoding='utf-8'执行此操作。

  • 任何可以在该编码中编码的内容都将作为编码字符串输出,任何不能作为实体“转义”的内容。使用encoding="ascii"会强制任何非ascii字符成为您想要的“漂亮”实体。

放在一起,这意味着:首先将字符串解析为元素(或树,如果您愿意),清理它,并根据需要对其进行序列化:

html = lxml.html.fromstring("<div><!-- word style><bleep><omgz 1,000 tags><--><p>It&#146;s a spicy meatball!</div>")
html = clean_html(html)
result = lxml.html.tostring(html, encoding="ascii")

(稍微脏一点的技巧是在unicode字符串的encode()方法上使用errors参数:尝试使用s.encode('ascii', 'xmlcharrefreplace')编码包含“特殊”字符的unicode字符串,看看它的作用。 ..)

答案 1 :(得分:2)

我认为&#146;应该是引号。使用chr(146)解码的字节值为146的cp1252的str对象是引号:

In [46]: print(chr(146).decode('cp1252'))
’

所以,你可以这样做:

import lxml.html.clean as clean
import re

html = "<div><!-- word style><bleep><omgz 1,000 tags><--><p>It&#146;s a spicy meatball!</div>"

html=re.sub('&#(\d+);',lambda m: chr(int(m.group(1))).decode('cp1252'),html)
print(html)
# <div><!-- word style><bleep><omgz 1,000 tags><--><p>It’s a spicy meatball!</div>
print(type(html))
# <type 'unicode'>
print(clean.clean_html(html))
# <div><p>It’s a spicy meatball!</p></div>

或者,

doc=lh.fromstring(html)
clean.clean(doc)

请注意,引号的unicode代码点值为8217.即ord(chr(146).decode('cp1252'))等于8217,因此lh.tostring返回:

print(lh.tostring(doc))
# <div><p>It&#8217;s a spicy meatball!</p></div>   

您可以在cp1252中重新编码,如下所示:

print(repr(lh.tostring(doc,encoding='cp1252')))
# '<div><p>It\x92s a spicy meatball!</p></div>'

我不知道如何哄骗lxml返回

'<div><p>It&#146;s a spicy meatball!</p></div>'

匹配BeautifulSoup代码的输出。好吧,显然它可以用正则表达式完成(颠倒我上面做的),但我不知道这是否必要或可取,因为lxml应该已经返回其他应用程序可以理解的html。

result=re.sub('&#(\d+);',lambda m: '&#{n};'.format(
    n=ord(unichr(int(m.group(1))).encode('cp1252'))),
            lh.tostring(doc))
print(result)
# <div><p>It&#146;s a spicy meatball!</p></div>

答案 2 :(得分:1)

您也可以将utf8字符串转换为带有xml字符的ascii

result = result.decode('utf-8').encode('ascii', 'xmlcharrefreplace')