python - unicode 和 python 请求发生了一些有趣的事情

标签 python python-2.7 unicode utf-8 python-requests

首先请注意,u'\xc3\xa8' 是具有 2 个代码点的 python2 unicode 字符串,è。接下来注意'\xc3\xa8'是代表字符è的utf8编码的python2字节str。所以 u'\xc3\xa8''\xc3\xa8',尽管看起来非常相似,但却是两个截然不同的野兽。

现在,如果我们尝试在浏览器中访问 https://www.sainsburys.co.uk/shop/gb/groceries/chablis/chablis-premièr-cru-brocard-75cl 所有应该顺利。

如果我在 ipython session 中定义:

unicode_url = u'https://www.sainsburys.co.uk/shop/gb/groceries/chablis/chablis-premièr-cru-brocard-75cl'

然后我可以打印它并看到我在浏览器的 URL 栏中输入的相同内容,太棒了。让我们尝试使用 python 请求获取它。

首先,我天真地只是尝试抛出 unicode url 以查看请求是否可以处理它:requests.get(unicode_url)。不,404,没问题,URL 应该被编码,所以我尝试 requests.get(unicode_url.encode('utf8'))。又不是404。没问题,也许我也需要做 URL 编码,所以我尝试 requests.get(urllib.quote(unicode_url.encode('utf8')))....它不喜欢那根本。

不过,回想起我一开始提到的 unicode 和 byte str 对象的相似之处,我也尝试过:

  requests.get('http://www.sainsburys.co.uk/shop/gb/groceries/chablis/chablis-premièr-cru-brocard-75cl')

令我惊讶的是它有效并成功给出了 200。

这里的请求是怎么回事?

编辑:就像另一个实验(这次在 Scrapy shell 中)

   from scrapy.http import Request
   unicode_url = u'https://www.sainsburys.co.uk/shop/gb/groceries/chablis/chablis-premièr-cru-brocard-75cl'
   fetch(Request(unicode_url))

绝对没问题!那么为什么 Scrapy 和浏览器可以毫无问题地处理它而不是 python 请求?以及为什么备用 url 在 python 请求中有效,但在浏览器或 Scrapy 中无效。

Latin1 与 UTF8

这也是事实

print unicode_url.encode('utf8').decode('latin1')
u'https://www.sainsburys.co.uk/shop/gb/groceries/chablis/chablis-premièr-cru-brocard-75cl'

一般来说,我相信仅对于拉丁 unicode 字符来说,如果你有一个像 u'\xe8' 这样的 unicode str,那么你可以将它转换为相同形式的字节 str编码为 latin1,即 u'è'=u'\xe8'u'\xe8'.encode('latin1') = '\xe8'(对象右边是 latin1 中的字节 str 编码,其形式与表示 è)

的 unicode 代码点相同

所以

In [95]: print u'è'.encode('utf8').decode('latin1')
è

同样,

In [94]: print u'è'.encode('latin1').decode('utf8')
è

不知是不是罪魁祸首

def prepare_url(self, url, params):
    """Prepares the given HTTP URL."""
    #: Accept objects that have string representations.
    #: We're unable to blindly call unicode/str functions
    #: as this will include the bytestring indicator (b'')
    #: on python 3.x.
    #: https://github.com/kennethreitz/requests/pull/2238
    if isinstance(url, bytes):
        url = url.decode('utf8')
    else:
        url = unicode(url) if is_py2 else str(url)

来自 requests/models.py

最佳答案

问题是站点上的 URL 实际上使用 latin1 编码来表示“è”字符 - 出于某种原因,Python 2 请求库试图在创建之前“自动清理 url”调用,在 utf-8 中对“è”字符进行编码 - 这就是导致 404 错误的原因。

在调用 requests.get 之前尝试将 unicode_url 编码为 latin1 也无济于事 - 它试图在“清理”之前将其解码为 un​​icode,并且它们在无效的 utf=8 序列上出错,该序列是“è"使用 latin-1 时("\xe8"字符)。

在这一点上值得注意的是,使用 Python 3 的请求也完全没有问题——因为该语言自动处理文本,请求需要更少的文本编码来回跳动——在我第一次尝试使用 Python 3 时,我只是得到:

In [13]: requests.get(unicode_url)
Out[13]: <Response [200]>

现在,很难找到 Python 2.7 和请求的解决方法 - 无需猴子修补请求中的某些特定代码以使其做正确的事情。然而,即使在 Python2 中,使用手动编码为 latin-1 的 unicode_url,并使用 urllib.open 而不是 requests 也可以工作——如果你真的需要 Python 2,也许这是最适合你的方法:

In [28]: a  = urllib.urlopen(unicode_url.encode("latin1"))

In [29]: a.code
Out[29]: 200

(真的 - 如果这只是您为某些特定工具所做的一些脚本,我建议您只切换到 Python 3.6 - 当您使用它时处理数据也会容易得多)

关于python - unicode 和 python 请求发生了一些有趣的事情,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43948639/

相关文章:

python - 如何将 '-'字符串解析到node js本地脚本?

python - 将 XPath 与 Scrapy 结合使用

python - 实现装饰器,它将用另一个实现替换一个类

java - 如何将具有Unicode编码的字符串转换为字母字符串

java - 如何处理来自外部源的分解 unicode 并将其存储在 postgresql 中

python - GAE NDB "Result cannot be set twice"错误

Python 数据帧操作

python - 如何填充matplotlib中线之间的区域

python - 优化 Keys 列表的字典成员的性能

Oracle 神秘的 Unicode 代码点