Scrapy XPath 选择器文本的 Unicode 和 UTF-8 编码问题
我正在使用Scrapy和Python(作为Django项目的一部分)来抓取一个包含德语内容的网站。我已经安装了libxml2
,作为Scrapy选择器的后端。
当我通过选择器提取单词'Hüftsitz'
(这是网站上显示的样子)时,我得到的是:u'H\ufffd\ufffdftsitz'
(Scrapy的XPath选择器返回的是Unicode字符串)。
如果我把这个编码成UTF-8
,我得到的是:'H\xef\xbf\xbd\xef\xbf\xbdftsitz'
。然后如果我打印出来,我看到的是'H??ftsitz'
,这显然是不对的。我在想这可能是什么原因。
网站的character-set
设置为UTF-8
。我在一个Python命令行中测试上述内容,sys.getdefaultencoding
设置为UTF-8
。在使用Django应用程序时,XPath选择器提取的数据写入一个使用UTF-8
字符集的MySQL数据库,我也观察到了同样的情况。
我是不是忽略了什么明显的东西?任何线索或帮助都将非常感激。
3 个回答
U+FFFD是一个替代字符,当你用some_bytes.decode('some-encoding', 'replace')
来解码时,如果some_bytes
中的某些部分无法被解码,就会出现这个字符。
你会看到两个这样的字符:u'H\ufffd\ufffdftsitz'
... 这表示u-变音符号被表示成了两个字节,而这两个字节都无法解码。很可能这个网站是用UTF-8编码的,但软件却试图用ASCII来解码。通常,当出现意外的Unicode转换时,就会用ASCII作为默认编码来解码。但是在这种情况下,使用'replace'
这个参数并不常见。更有可能的是,代码是由一个认为“不会抛出异常”就等于“能正常工作”的人写的。
请编辑你的问题,提供网址,并展示出能产生u'H\ufffd\ufffdftsitz'
的最小代码。
u'\ufffd' 是一种叫做 “unicode替换字符” 的东西,通常在显示时会变成一个黑色三角形里的问号。它不是一个变音符号。所以问题可能出在更早的地方。你需要检查网页的头部信息,看看它说的编码是什么,并确认它确实是那个编码。
这个unicode替换字符通常是用来替代一些非法或无法识别的字符,出现这种情况可能有很多原因,但最可能的原因是编码和它所声称的并不一致。
非常感谢你们的回答,约翰和史蒂文。你们的回答让我换了个思路,这让我找到了问题的根源,也找到了一个有效的解决方案。
我在使用以下测试代码:
import urllib
import urllib2
from scrapy.selector import HtmlXPathSelector
from scrapy.http import HtmlResponse
URL = "http://jackjones.bestsellershop.com/DE/jeans/clark-vintage-jos-217-sup/37246/37256"
url_handler = urllib2.build_opener()
urllib2.install_opener(url_handler)
handle = url_handler.open(URL)
response = handle.read()
handle.close()
html_response = HtmlResponse(URL).replace(body=response) # Problematic line
hxs = HtmlXPathSelector(html_response)
desc = hxs.select('//span[@id="attribute-content"]/text()')
desc_text = desc.extract()[0]
print desc_text
print desc_text.encode('utf-8')
在Scrapy的命令行工具里,当我提取描述数据时,结果是正常的。这让我怀疑我的代码可能有问题,因为在pdb
提示符下,我看到提取的数据中有替换字符。
我查看了Scrapy的文档,特别是关于Response类的部分,然后把上面的代码调整成了这样:
import urllib
import urllib2
from scrapy.selector import HtmlXPathSelector
from scrapy.http import HtmlResponse
URL = "http://jackjones.bestsellershop.com/DE/jeans/clark-vintage-jos-217-sup/37246/37256"
url_handler = urllib2.build_opener()
urllib2.install_opener(url_handler)
handle = url_handler.open(URL)
response = handle.read()
handle.close()
#html_response = HtmlResponse(URL).replace(body=response)
html_response = HtmlResponse(URL, body=response)
hxs = HtmlXPathSelector(html_response)
desc = hxs.select('//span[@id="attribute-content"]/text()')
desc_text = desc.extract()[0]
print desc_text
print desc_text.encode('utf-8')
我做的改动是把这一行html_response = HtmlResponse(URL).replace(body=response)
改成了html_response = HtmlResponse(URL, body=response)
。我的理解是,replace()
这个方法在处理特殊字符时,可能在编码上出现了问题。
如果有人能详细解释一下replace()
方法到底哪里出错了,我会非常感激。
再次感谢大家。