Requests是用Python语言编写,使用的是urllib3,拥有了它的所有特性,Requests 支持 HTTP 连接保持和连接池,支持使用 cookie 保持会话,支持文件上传,支持自动确定响应内容的编码,支持国际化的 URL 和 POST 数据自动编码。现代、国际化、人性化。正是由于它的强大,所以现在被广泛的用于爬虫等方面,但是在对中文网页进行抓取的时候,或多或少会遇到一些编码问题,本文就这些问题讨论一下。
简单demo
1 | import requests |
请求发出后,Requests默认会基于 HTTP 头部对响应的编码作出有根据的推测。当你使用 r.text 获取返回的内容时,Requests会使用其推测的编码格式进行解码。而当使用 r.content,则会获取原始的字节方式的响应体。
Requests默认识别编码
在 C:\Python27\Lib\site-packages\requests\utils.py
中,发现默认encoding的获取方式1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21# Try charset from content-type
encoding = get_encoding_from_headers(r.headers)
def get_encoding_from_headers(headers):
"""Returns encodings from given HTTP Header Dict.
:param headers: dictionary to extract encoding from.
"""
content_type = headers.get('content-type')
if not content_type:
return None
content_type, params = cgi.parse_header(content_type)
if 'charset' in params:
return params['charset'].strip("'\"")
if 'text' in content_type:
return 'ISO-8859-1'
在函数get_encoding_from_headers,如果返回头content-type
发现”charset”,则返回相应的编码格式,否则返回”ISO-8859-1”。利用这种方式判断编码方式,未免有点过于简单和草率。
Requests其他识别编码方式
在知道Requests的默认识别方式不靠谱后,通常我们采用的方式有
- 提取返回体中
META http-equiv=Content-Type content="text/html; charset=gb2312
中的编码格式- 利用chardet库识别返回体的编码格式
其实Requests中,已经支持这两种识别方式。见utils.py
中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18def get_encodings_from_content(content):
"""Returns encodings from given content string.
:param content: bytestring to extract encodings from.
"""
warnings.warn((
'In requests 3.0, get_encodings_from_content will be removed. For '
'more information, please see the discussion on issue #2266. (This'
' warning should only appear once.)'),
DeprecationWarning)
charset_re = re.compile(r'<meta.*?charset=["\']*(.+?)["\'>]', flags=re.I)
pragma_re = re.compile(r'<meta.*?content=["\']*;?charset=(.+?)["\'>]', flags=re.I)
xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]')
return (charset_re.findall(content) +
pragma_re.findall(content) +
xml_re.findall(content))
再见models.py
,定义了apparent_encoding属性1
2
3
4
def apparent_encoding(self):
"""The apparent encoding, provided by the chardet library"""
return chardet.detect(self.content)['encoding']
三种识别方式比较
针对这三种方式,有的需要获取返回体,所以这三种识别编码的速率及需要的资源不同。先看看下面的程序:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36#! usr/bin/env python
#! coding: utf-8
import requests
import time
import re
r = requests.get("http://www.scusec.org")
def charset_type():
char_type = requests.utils.get_encoding_from_headers(r.headers)
return char_type
def charset_content():
charset_content = requests.utils.get_encodings_from_content(r.content)
return charset_content[0]
def charset_det():
charset_det = r.apparent_encoding
return charset_det
def chose_fun(n):
if n == 1:
return charset_type
elif n == 2:
return charset_content
else:
return charset_det
if __name__ == '__main__':
for i in range(1,4):
funs = chose_fun(i)
start_time = time.time()
chars = funs()
end_time = time.time()
times = end_time - start_time
print chars+" "+str(times)
返回结果
可见在速率上charset_type > charset_content > charset_det
,并且charset_type
在空间使用上也是最优的。
综合的解决方法
综合上面的几种解决方式,可以定义一个解决函数:1
2
3
4
5
6
7
8
9def charsets(res):
_charset = requests.utils.get_encoding_from_headers(res.headers)
if _charset == 'ISO-8859-1':
__charset = requests.utils.get_encodings_from_content(res.content)[0]
if __charset:
_charset = __charset
else:
_charset = res.apparent_encoding
return _charset
那么处理中文网页:
- 利用
charsets
函数获取页面编码方式,再配合r.content
进行相关的解码和编码 - 利用
charsets
函数获取页面编码方式,设定r.encoding
的值,再获取r.text