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

Posted

技术标签:

【中文标题】使用python编辑html,但lxml将漂亮的html实体转换为奇怪的编码【英文标题】:Using python to edit html, but lxml converts nice html entities to strange encoding 【发布时间】:2011-06-20 02:07:39 【问题描述】:

我正在尝试使用 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 仍然存在。

是的,我会告诉人们停止使用 word 来编写 html,但我会听到整个

“我喜欢它,你不能把我当成hitlr”。

编辑:一个漂亮的汤解决方案:

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

【问题讨论】:

好奇:你为什么不用 BeautifulSoup @inspector:据我了解,BeautifulSoup 已不再积极开发,鼓励用户寻求其他解决方案(如 lxml)。 @SethJohnson:我不知道。谢谢你告诉我 【参考方案1】:

有几件事——如果你知道的话——会导致最简单/最好的解决方案:

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

您可以控制 Element 或 ElementTree 的序列化方式,方法是为 lxml.html.tostring() 方法或树的 write() 方法提供编码选项(顺便说一下,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 字符串进行编码,看看会发生什么......)

【讨论】:

【参考方案2】:

我假设&amp;#146; 应该是一个引号。字节值为146的str对象chr(146),解码为cp1252是引号:

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>

【讨论】:

【参考方案3】:

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

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

【讨论】:

以上是关于使用python编辑html,但lxml将漂亮的html实体转换为奇怪的编码的主要内容,如果未能解决你的问题,请参考以下文章

将 python 脚本转换为使用 lxml 的 etree 模块的 linux 二进制文件的问题

如何使用缩进将 HTML 漂亮地打印到文件中

Python:使用`lxml.html`将HTML内容注入标签

python怎么安装lxml html 解析器

使用 python 解析 HTML 表 - HTMLparser 或 lxml

Python操作XML和HTML,LXML类库的使用