Requests是用python语言编写,使用的是urllib3,拥有了它的所有特性,Requests 支持 HTTP 连接保持和连接池,支持使用 cookie 保持会话,支持文件上传,支持自动确定响应内容的编码,支持国际化的 URL 和 POST 数据自动编码。现代、国际化、人性化。正是由于它的强大,所以现在被广泛的用于爬虫等方面,但是在对中文网页进行抓取的时候,或多或少会遇到一些编码问题,本文就这些问题讨论一下。
简单demoimport requests r = requests.get("http://www.scusec.org") print r.encoding # Requests推测使用的编码格式 print r.text # 字符串方式的响应体,会自动根据响应头部的字符编码进行解码 print r.content # 字节方式的响应体,会自动为你解码 gzip 和 deflate 压缩
请求发出后,Requests默认会基于 HTTP 头部对响应的编码作出有根据的推测。当你使用 r.text 获取返回的内容时,Requests会使用其推测的编码格式进行解码。而当使用 r.content,则会获取原始的字节方式的响应体。
Requests默认识别编码在 C:\Python27\Lib\site-packages\requests\utils.py 中,发现默认encoding的获取方式
# 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 中:
def 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属性
@property def apparent_encoding(self): """The apparent encoding, provided by the chardet library""" return chardet.detect(self.content)['encoding'] 三种识别方式比较针对这三种方式,有的需要获取返回体,所以这三种识别编码的速率及需要的资源不同。先看看下面的程序:
#! 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 在空间使用上也是最优的。
综合的解决方法综合上面的几种解决方式,可以定义一个解决函数:
def 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