以抓取网易图片的程序为例说明python的编码问题,主要涉及python中文乱码,unicode和str类型转换,python解释器处理编码方式等问题。
1 #coding:utf-8 2 import sys 3 reload(sys) 4 sys.setdefaultencoding('utf-8') 5 6 import re 7 import requests 8 import os 9 import urllib 10 11 def down_pic(url): 12 pic_html = requests.get(url) 13 if pic_html.status_code == 200: 14 pic_html_text = pic_html.text 15 #print pic_html.encoding 16 #print pic_html_text 17 18 #获取所有图片url,选择图片均为大图 19 pic_gallery_patt = r'"oimg": "(.+?.jpg)"' 20 #获取图片链接的主题 21 title_patt = r'<title>(.+?)</title>' 22 #获取图片的名称 23 img_name_patt = r'"id": "(.+?)",' 24 25 img_text = re.findall(pic_gallery_patt, pic_html_text) 26 title_name = re.findall(title_patt, pic_html_text) 27 file_name = re.findall(img_name_patt, pic_html_text) 28 29 #创建文件夹,需要处理转义符 30 curr_path = os.getcwd() 31 curr_path = curr_path.replace('\', '/') 32 file_dir = curr_path + '/' 33 if os.path.exists(file_dir): 34 file_dir += title_name[0] 35 file_dir += '/' 36 #curr_path 是str类型,title_name[0]是unicode类型 37 #print type(file_dir) 38 #直接将unicode作为参数传入mkdir()方法,Python将先使用源代码文件声明的字符编码进行编码然后写入 39 os.mkdir(file_dir) 40 41 print '开始下载......' 42 43 for dic in zip(img_text,file_name): 44 #requests模块抓取的网页内容为unicode类型,可以用encode可以转换为utf-8编码的str类型 45 img_utf8_url = dic[0] 46 #生成图片存储路径 47 file_name_str = dic[1] 48 file_type = ".jpg" 49 #unicode类型和str类型连接生成新对象为unicode类型 50 filepath = file_dir + file_name_str + file_type 51 print img_utf8_url, filepath 52 #filepath为unicode类型,normcase函数会把filepath自动处理成系统可以使用的字符串 53 #filepath = os.path.normcase(filepath) 54 urllib.urlretrieve(img_utf8_url, filepath) 55 56 print '下载完成......' 57 58 59 if __name__ == '__main__': 60 pic_url = r'http://news.163.com/photoview/00AP0001/37116.html?from=tj_day#p=96A9I01H00AP0001' 61 down_pic(pic_url)
1.python中的str和unicode
首先,程序的第一步是用requests模块获取网页,pic_html.text获得响应内容,以 unicode表示(如果用pic_html.content,内容将以bytes表示)。
file_dir += title_name[0]是为了生成文件目录,但是file_dir是str类型,title_name[0]是unicode类型,连接之后是什么类型呢?用type函数查看file_dir发现是unicode类型。说明一个unicode类型和str类型连接得到的是unicode类型。
下面解释一下str和unicode:
str和unicode都是basestring的子类。严格意义上说,str其实是字节串,它是unicode经过编码后的字节组成的序列。对UTF-8编码的str'汉'使用len()函数时,结果是3,因为实际上,UTF-8编码的'汉' == 'xE6xB1x89'。
unicode才是真正意义上的字符串,对字节串str使用正确的字符编码进行解码后获得,并且len(u'汉') == 1。
我们看一下unicode的解释:
Unicode字符串
Unicode是书写国际文本的标准方法。如果你想要用你的母语如北印度语或阿拉伯语写文本,那么你需要有一个支持Unicode的编辑器。类似地,Python允许你处理Unicode文本——你只需要在字符串前加上前缀u或U。例如,u"This is a Unicode string."。
记住,在你处理文本文件的时候使用Unicode字符串,特别是当你知道这个文件含有用非英语的语言写的文本。
str其实是字节串,它是unicode字符串经过编码(encode)后的字节组成的序列。unicode是str字符串经过解码(encode)后的字符串
encode和decode的使用:
(unicode字符串).encode是指将unicode转换成其他格式的编码
例:u'中文'.encode('utf8')将unicode字符串(u'中文')转换成utf8编码
(str字符串).decode是指将其他格式的编码转换成unicode
例:'中文'.decode('utf8')将utf8字符串转换成unicode编码
就像下面这样使用
1 s = '你好' 2 print s 3 s.decode('utf-8') 4 print s 5 s1 = u'你好' 6 print s1 7 s1 = s1.encode('utf-8') 8 print s1
但是如果你直接在IDLE这样用就会报错,因为终端的默认编码设置为ascii,即s的编码是ascii,然而decode('utf-8')却是将utf8字符串转换成unicode编码。
如果decode('ascii')呢?由于ASCII无法将中文转成unicode,所以它仍然会报错了。
2. python中文乱码
1 s1 = u'你好' 2 print s1 3 s1 = s1.encode('utf-8') 4 print s1
在IDLE中,print s1会输出乱码,因为print只是把字节串传递给操作系统,而由操作系统决定以何种编码输出。对于unicode,就依赖于stdout的输出编码。由于stdout的默认输出编码为ascii,所以出错。同样s1转码后,以ascii编码输出utf8编码的字符串也会出错。
如果你要处理的程序中有中文,最好在开头加上:
1 #coding:utf-8 2 import sys 3 reload(sys) 4 sys.setdefaultencoding('utf-8')
推荐utf-8,不推荐cp936。
建议: 使用字符编码声明,并且同一工程中的所有源代码文件使用相同的字符编码声明。
如果你用sublime text2编写python程序,可能sublime text2已经把编码默认设为utf-8。但是为了万无一失,同样在文件头加上上述代码。
不管是str还是unicode,字符串在python内部的表示都是unicode编码,它相当于一种统一的中间编码。所以经常会需要从其他编码成unicode,或者从unicode解码到其他编码。这就意味着,对于代码文件,或者其他内容,不管这些内容的编码是ASCII还是UTF-8,python都会把这些内容转换成它所接受的编码—即unicode。
python解析器读取代码文件本身时,会试图把它转成unicode。但是上面的代码文件中出现了中文,并且文件头上没有任何的编码声明,python就会试图采用默认的ASCII编码来把中文转成unicode,但是显然,ASCII无法将中文转成unicode,所以它报错了。
在文件的第一行加上声明:#coding=编码字符串,注意声明的编码必须与文件实际保存的编码一致
3.为什么os.mkdir()和urllib.urlretrieve(url, filepath)能够接受unicode类型?
可以参考对python读写文件的解释:
open()打开文件时,read()读取的是str,读取后需要使用正确的编码格式进行decode()。
write()写入文件时,如果参数是 unicode,则需要使用你希望写入的编码进行encode();如果是其他编码格式的str,则需要先用该str的编码进行decode(),转成 unicode后再使用写入的编码进行encode();如果直接将unicode作为参数传入write()方法,Python将先使用源代码文件声明的字符编码进行编码然后写入。
os.mkdir()和urllib.urlretrieve(url, filepath)同理。