首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >不要相信requests返回的text

不要相信requests返回的text

作者头像
小小詹同学
发布2019-11-12 12:49:31
4680
发布2019-11-12 12:49:31
举报
文章被收录于专栏:小詹同学小詹同学

本文投稿自公众号:猿人学python

Python的requests库是一个非常好用的库,这应该已经是大多写过爬虫的人的共识了。它的简洁易用给我们带来很大方便。然而,它也并不是非常完美。今天我们就说说它在处理中文编码方面的不足。

requests的使用非常简单,如下:

一句函数调用,就可以获得请求结果的对象response,通过response.content 可以得到原始的二进制数据,通过response.text可以得到解码后的文本数据,解码是根据response.encoding进行的。然而,requests对这个encoding(编码)的获取是有问题的。

它获取编码的过程分为两步,不幸的是每一步都有问题:

第一步:从http返回的headers里面找编码。

这一步的代码在源文件utils.py里面是get_encoding_from_headers(headers)函数:

最后两行代码,它认为headers里面的‘Content-Type’包含‘text’就是‘ISO-8859-1’编码。这种想法是不严谨的。

我们用chrome浏览器打开最开始代码中的那个网址,这是一个中文网页:

http://epaper.sxrb.com/

在用Chrome的F12查看http响应的头,如下:

这个网站给出的Content-Type不是下面的正规格式:

Content-Type: text/html; charset=UTF-8

然后,requests的get_encoding_from_headers函数就得到了ISO-8859-1的编码,再用这个编码去解码中文,当然就会出现乱码。

第二步:如果不能从响应headers得到编码,就用chardet从二进制的content猜测

严格讲,这步出现的编码问题不是requests的,而是chardet的,就判requests一个失察之责吧。

在requests的源码models.py中定义了requests.get()返回的类Response。我们再看看其中text()的定义:

响应头找不到编码时,self.encoding就是None。它就会通过self.apparent_encoding获得编码,那就再看看这个apparent_encoding是怎么来的:

很简单,就是通过chardet检测的。问题就出现在这个chardet上面。那我们就打破砂锅问到底,去看看chardet的代码。

上图是chardet的全部源代码。其中处理国标中文编码的gb2312开头的两个文件。我们用grep再看看全部代码中含有gb的部分:

grep -i gb *py

以上说明,chardet对国标中文编码返回的就是(只是)GB2312。那么问题就来了,国标不只是GB2312,还有GBK,GB18030编码。

(1)GB 2312 标准共收录 6763 个汉字

(2)GBK 即汉字内码扩展规范,共收入 21886 个汉字和图形符号,兼容GB2312

(3)GB 18030 与 GB 2312-1980 和 GBK 兼容,共收录汉字70244个

由此可知,三种国标中文编码的汉字个数是如下关系:

GB2312 < GBK < GB18030

如果不属于GB2312的汉字用GB2312去编解码会出现上面问题呢?我们来做个实验:

例子中的“镕”字不在GB2312中,用这个编码时就会报错,用GBK编码后的二进制数据再用GB2312解码时同样会报错,都是因为“镕”不是GB2312里面的汉字。

这时候,我们像requests那样把errors设置为replace再用GB2312解码得到的文本就会有乱码出现,“镕”字变成乱码了。

最后我们用chardet检验二进制数据的编码,得到的是GB2312,但应该是GBK或GB18030编码。当然,chardet的这个bug已经有人在github提出issues,最早是2014年的#33, 后来有#99,#168,但是不懂中文的老外一直没有merge到master。

问题弄明白了,那么建议是什么呢?在爬虫中,尤其是抓取中文网页(非英文网页)时用cchardet检验response.content,而不是直接用response.text。

cchardet是uchardet的Python绑定,后者是用C++实现的字符编码检测库,来自Mozilla组织,质量过硬,速度更快,值得信赖。

望学有所获 !每天进步一点点 !

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小詹学Python 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档