Scrapy XPath 选择器文本的 Unicode 和 UTF-8 编码问题

3 投票
3 回答
16407 浏览
提问于 2025-04-16 15:31

我正在使用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 个回答

1

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'最小代码。

3

u'\ufffd' 是一种叫做 “unicode替换字符” 的东西,通常在显示时会变成一个黑色三角形里的问号。它不是一个变音符号。所以问题可能出在更早的地方。你需要检查网页的头部信息,看看它说的编码是什么,并确认它确实是那个编码。

这个unicode替换字符通常是用来替代一些非法或无法识别的字符,出现这种情况可能有很多原因,但最可能的原因是编码和它所声称的并不一致。

3

非常感谢你们的回答,约翰和史蒂文。你们的回答让我换了个思路,这让我找到了问题的根源,也找到了一个有效的解决方案。

我在使用以下测试代码:

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()方法到底哪里出错了,我会非常感激。

再次感谢大家。

撰写回答