解决Requests中文乱码

来源:互联网 发布:汽车传动比知乎 编辑:程序博客网 时间:2024/06/16 01:09

都在推荐用Requests库,而不是Urllib,但是读取网页的时候中文会出现乱码。Python Requests 在作为代理爬虫节点抓取不同字符集网站时会遇到一些问题,如中文乱码。如果单纯的抓取微博,微信,电商,那么字符集charset是很容易被确认的,你甚至可以单方面吧encoding给固定住。但作为舆情数据来说,他每天要抓取几十万个不同网站的敏感数据,所以这就需要我们更好确认字符集编码,避免中文的乱码情况。

我们首先看这个例子. 你会发现一些有意思的事情. 

第一个问题是,为什么会有ISO-8859-1这样的字符集编码?

iso-8859是什么?  他又被叫做Latin-1或“西欧语言” .  对于我来说,这属于requests的一个bug,在requests库的github里可以看到不只是中国人提交了这个issue.  但官方的回复说是按照http rfc设计的。

下面通过查看requests源代码,看这问题是如何造成的 !

requests会从服务器返回的响应头的 Content-Type 去获取字符集编码,如果content-type有charset字段那么requests才能正确识别编码,否则就使用默认的 ISO-8859-1. 一般那些不规范的页面往往有这样的问题. 

文件: requests.utils.py

第二个问题, 那么如何获取正确的编码? 

requests的返回结果对象里有个apparent_encoding函数, apparent_encoding通过调用chardet.detect()来识别文本编码. 但是需要注意的是,这有些消耗计算资源.
至于为毛,可以看看chardet的源码实现. 

第三个问题,requests的text() 跟 content() 有什么区别? 

requests在获取网络资源后,我们可以通过两种模式查看内容。 一个是r.text,另一个是r.content,那他们之间有什么区别呢?

分析requests的源代码发现,r.text返回的是处理过的Unicode型的数据,而使用r.content返回的是bytes型的原始数据。也就是说,r.content相对于r.text来说节省了计算资源,r.content是把内容bytes返回. 而r.text是decode成Unicode. 如果headers没有charset字符集的话,text()会调用chardet来计算字符集,这又是消耗cpu的事情.

通过看requests代码来分析text() content()的区别.


分析: 
r = requests.get(“http://www.baidu.com“) 
**r.text返回的是Unicode型的数据。 
使用r.content返回的是bytes型的数据。 
也就是说,如果你想取文本,可以通过r.text。 
如果想取图片,文件,则可以通过r.content。**

获取一个网页的内容

方法1:使用r.content,得到的是bytes型,再转为str

url='http://music.baidu.com'r = requests.get(url)html=r.contenthtml_doc=str(html,'utf-8') #html_doc=html.decode("utf-8","ignore")print(html_doc)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

方法2:使用r.text 
Requests 会自动解码来自服务器的内容。大多数 unicode 字符集都能被无缝地解码。请求发出后,Requests 会基于 HTTP 头部对响应的编码作出有根据的推测。当你访问 r.text 之时,Requests 会使用其推测的文本编码。你可以找出 Requests 使用了什么编码,并且能够使用 r.encoding 属性来改变它. 
但是Requests库的自身编码为: r.encoding = ‘ISO-8859-1’ 
可以 r.encoding 修改编码

url='http://music.baidu.com'r=requests.get(url)r.encoding='utf-8'print(r.text)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

获取一个网页的内容后存储到本地

方法1:r.content为bytes型,则open时需要open(filename,”wb”)

r=requests.get("music.baidu.com")html=r.contentwith open('test5.html','wb') as f:    f.write(html)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

方法2:r.content为bytes型,转为str后存储

r = requests.get("http://www.baidu.com")html=r.contenthtml_doc=str(html,'utf-8') #html_doc=html.decode("utf-8","ignore")# print(html_doc)with open('test5.html','w',encoding="utf-8") as f:    f.write(html_doc)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

方法3:r.text为str,可以直接存储

r=requests.get("http://www.baidu.com")r.encoding='utf-8'html=r.textwith open('test6.html','w',encoding="utf-8") as f:    f.write(html)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

Requests+lxml

# -*-coding:utf8-*-import requestsfrom lxml import etreeurl="http://music.baidu.com"r=requests.get(url)r.encoding="utf-8"html=r.text# print(html)selector = etree.HTML(html)title=selector.xpath('//title/text()')print (title[0])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

结果为:百度音乐-听到极致

终极解决方法

以上的方法虽然不会出现乱码,但是保存下来的网页,图片不显示,只显示文本。而且打开速度慢,找到了一篇博客,提出了一个终极方法,非常棒。

来自博客 
http://blog.chinaunix.net/uid-13869856-id-5747417.html的解决方案:

# -*-coding:utf8-*-import requestsreq = requests.get("http://news.sina.com.cn/")if req.encoding == 'ISO-8859-1':    encodings = requests.utils.get_encodings_from_content(req.text)    if encodings:        encoding = encodings[0]    else:        encoding = req.apparent_encoding    # encode_content = req.content.decode(encoding, 'replace').encode('utf-8', 'replace')    global encode_content    encode_content = req.content.decode(encoding, 'replace') #如果设置为replace,则会用?取代非法字符;print(encode_content)with open('test.html','w',encoding='utf-8') as f:    f.write(encode_content)

对于requests中文乱码解决方法有这么几种. 

方法一: 

由于content是HTTP相应的原始字节串,可以根据headers头部的charset把content decode为unicode,前提别是ISO-8859-1编码.


另外有一种特别粗暴方式,就是直接根据chardet的结果来encode成utf-8格式. 

如果在确定使用text,并已经得知该站的字符集编码时,可以使用 r.encoding = ‘xxx’ 模式, 当你指定编码后,requests在text时会根据你设定的字符集编码进行转换. 

方法二:

根据我抓几十万的网站的经验,大多数网站还是很规范的,如果headers头部没有charset,那么就从html的meta中抽取.

python requests的utils.py里已经有个完善的从html中获取meta charset的函数. 说白了还是一对的正则表达式.


文件: utils.py

最后,针对requests中文乱码的问题总结:

统一编码,要不都成utf-8, 要不就用unicode做中间码 ! 

国内的站点一般是utf-8、gbk、gb2312  , 当requests的encoding是这些字符集编码后,是可以直接decode成unicode. 

但当你判断出encoding是 ISO-8859-1 时,可以结合re正则和chardet判断出他的真实编码. 可以把这逻辑封装补丁引入进来.

Python3.x解决了这编码问题,如果你还是python2.6 2.7,那么还需要用上面的方法解决中文乱码的问题. 

参考代码分析Python requests库中文编码问题

      Python HTTP库requests中文页面乱码解决方案!

 
原创粉丝点击