使用lxml在Python中编码 - 复杂解决方案
我需要下载一个网页,并用lxml来解析它,然后生成UTF-8格式的XML输出。我觉得用伪代码来表示这个过程会更清晰:
from lxml import etree
webfile = urllib2.urlopen(url)
root = etree.parse(webfile.read(), parser=etree.HTMLParser(recover=True))
txt = my_process_text(etree.tostring(root.xpath('/html/body'), encoding=utf8))
output = etree.Element("out")
output.text = txt
outputfile.write(etree.tostring(output, encoding=utf8))
所以网页文件可能会用任何编码格式(lxml应该能处理这些)。而输出文件必须是UTF-8格式。我不太确定该在哪里使用编码。这个流程图可以吗?(我找不到关于lxml和编码的好教程,但我能找到很多相关的问题……)我需要一个稳健的解决方案。
编辑:
所以为了把UTF-8格式传给lxml,我使用了
converted = UnicodeDammit(webfile, isHTML=True)
if not converted.unicode:
print "ERR. UnicodeDammit failed to detect encoding, tried [%s]", \
', '.join(converted.triedEncodings)
continue
webfile = converted.unicode.encode('utf-8')
2 个回答
lxml的编码检测功能比较弱。
不过,要注意的是,网页最常见的问题是缺少编码声明(或者编码声明不正确)。因此,通常只用BeautifulSoup的编码检测功能,叫做UnicodeDammit,就足够了,剩下的交给lxml自己的HTML解析器,它的速度快很多。
我建议使用UnicodeDammit来检测编码,然后用lxml来解析网页。你还可以使用HTTP头中的Content-Type(需要提取charset=ENCODING_NAME)来更准确地检测编码。
在这个例子中,我使用BeautifulSoup4(你还需要安装chardet,这样可以更好地自动检测,因为UnicodeDammit内部使用了chardet):
from bs4 import UnicodeDammit
if http_charset == "":
ud = UnicodeDammit(content, is_html=True)
else:
ud = UnicodeDammit(content, override_encodings=[http_charset], is_html=True)
root = lxml.html.fromstring(ud.unicode_markup)
或者,为了让之前的回答更完整,你可以修改为:
if ud.original_encoding != 'utf-8':
content = content.decode(ud.original_encoding, 'replace').encode('utf-8')
为什么这样比单纯使用chardet要好呢?
你不会忽略Content-Type HTTP头
例如:Content-Type:text/html; charset=utf-8
你不会忽略http-equiv元标签。例子:
... http-equiv="Content-Type" content="text/html; charset=UTF-8" ...
除此之外,你还可以利用chardet、cjkcodecs和iconvcodec等编码工具,以及更多其他工具。
lxml在处理输入编码时可能会有点问题。最好是输入UTF8格式的数据,然后输出也是UTF8格式。
你可能想用一下chardet这个模块,或者UnicodeDammit来解码实际的数据。
你可以尝试做一些类似下面的事情:
import chardet
from lxml import html
content = urllib2.urlopen(url).read()
encoding = chardet.detect(content)['encoding']
if encoding != 'utf-8':
content = content.decode(encoding, 'replace').encode('utf-8')
doc = html.fromstring(content, base_url=url)
我不太明白你为什么要在lxml和etree之间切换,除非你在使用另一个已经用etree的库?