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

s h*_*ley 10 python lxml character-encoding html-parsing

我正在尝试使用python(使用pyquery和lxml)来改变和清理一些html.

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

lxml.html.clean函数clean_html()运行良好,除了它取代了很好的html实体,

&#146; 
Run Code Online (Sandbox Code Playgroud)

带一些unicode字符串

\xc2\x92
Run Code Online (Sandbox Code Playgroud)

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

我不能确定那里有哪些角色,所以我不能只使用

replace("\xc2\x92","&#146;").
Run Code Online (Sandbox Code Playgroud)

我试过用了

clean_html(html).encode('latin-1')
Run Code Online (Sandbox Code Playgroud)

但是unicode仍然存在.

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

"我喜欢它,因为你不能让我变成一个人".

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

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
Run Code Online (Sandbox Code Playgroud)

Ste*_*ven 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")
Run Code Online (Sandbox Code Playgroud)

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


unu*_*tbu 2

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

\n\n
In [46]: print(chr(146).decode(\'cp1252\'))\n\xe2\x80\x99\n
Run Code Online (Sandbox Code Playgroud)\n\n

所以,你可以这样做:

\n\n
import lxml.html.clean as clean\nimport re\n\nhtml = "<div><!-- word style><bleep><omgz 1,000 tags><--><p>It&#146;s a spicy meatball!</div>"\n\nhtml=re.sub(\'&#(\\d+);\',lambda m: chr(int(m.group(1))).decode(\'cp1252\'),html)\nprint(html)\n# <div><!-- word style><bleep><omgz 1,000 tags><--><p>It\xe2\x80\x99s a spicy meatball!</div>\nprint(type(html))\n# <type \'unicode\'>\nprint(clean.clean_html(html))\n# <div><p>It\xe2\x80\x99s a spicy meatball!</p></div>\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者,

\n\n
doc=lh.fromstring(html)\nclean.clean(doc)\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
print(lh.tostring(doc))\n# <div><p>It&#8217;s a spicy meatball!</p></div>   \n
Run Code Online (Sandbox Code Playgroud)\n\n

您可以将其重新编码为 cp1252,如下所示:

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

我不知道怎么哄lxml回来

\n\n
\'<div><p>It&#146;s a spicy meatball!</p></div>\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

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