最近研究搜索引擎、知識圖譜和Python爬蟲比較多,中文亂碼問題再次浮現於眼前。雖然市面上講述中文編碼問題的文章數不勝數,同時之前我也講述過PHP處理數據庫服務器中文亂碼問題,可是此處仍是準備簡單作下筆記。方便之後查閱和你們學習。
中文編碼問題的處理核心都是——保證全部的編碼方式一致便可,包括編譯器、數據庫、瀏覽器編碼方式等,而Python一般的處理流程是將unicode做爲中間轉換碼進行過渡。先將待處理字符串用unicode函數以正確的編碼轉換爲Unicode碼,在程序中統一用Unicode字符串進行操做;最後輸出時,使用encode方法,將Unicode再轉換爲所需的編碼便可,同時保證編輯器服務器編碼方式一致。
PS:固然Python3除外!這篇文章比較囉嗦,畢竟是在線筆記和體會嘛,望理解~
在詳細講解概念以前,先講述我最近遇到的字符編碼的兩個問題及解決。下圖是最多見到幾個問題編碼問題:html
參考資料:
詳解 python 中文編碼與處理
python字符編碼與解碼unicode、str和中文 'ascii' codec can't decode
Python的中文編碼問題-segmentfault
書籍《Python核心編程(第二版)》和《Python基礎教程(第二版)》
python
背景:在作Python定向圖片爬蟲時,會經過raw_input輸入關鍵詞如「主播」,會爬取標題title中包含"主播"的URL,再去到具體的頁面爬取圖集。
問題:若是是自定義字符串直接經過: s=u'主播' 定義爲Unicode編碼,再與一樣爲Unicode編碼的title.text(下一篇文章詳細介紹該爬蟲)比較便可。可是若是須要raw_input輸入呢?並且在經過unicode或decode轉換過程當中老是報錯,爲何呢?
主要問題是如何將str轉換爲unicode編碼(How to convert str to unicode),默認python編碼方式ascii碼。
unicode(string[, encoding[, errors]])程序員
>>> help(unicode) Help on class unicode in module __builtin__: class unicode(basestring) | unicode(object='') -> unicode object | unicode(string[, encoding[, errors]]) -> unicode object | | Create a new Unicode object from the given encoded string. | encoding defaults to the current default string encoding. | errors can be 'strict', 'replace' or 'ignore' and defaults to 'strict'.
舉個簡單的例子:須要判斷搜索詞key是否在title標題中。web
1 # coding=utf-8 2 import sys 3 4 def getTitle(key,url): 5 #title = driver.find_element_by_xpath() 6 title = u'著名女主播Miss與杰倫直播LOL' 7 print key,type(key) 8 print title,type(title) 9 if key in title: 10 print 'YES' 11 else: 12 print 'NO' 13 14 key = raw_input("Please input a key: ") 15 print key,type(key) 16 url = 'http://www.baidu.com/' 17 getTitle(key,url)
輸出以下圖所示:數據庫
嘗試修改的方法包括:經過unicode(key,'utf-8')轉碼、key.decode('utf-8')轉碼、重置sys.defaultencoding都不行。而經過key.decode('raw_unicode_escape')轉換獲得的亂碼"Ö÷²¥"(主播)。而同窗的Python2.7能將str轉換成unicode編碼。
"UnicodeDecodeError: 'ascii' codec can't decode byte" 需先將str轉換爲unicode編碼,可是我s.decode('utf-8')就報錯 "UnicodeDecodeError: 'utf8' codec can't decode byte"。編程
s = '主播' s.decode('utf-8').encode('gb18030')
最後解決方法從stackoverflow獲得,一方面說明本身確實研究得不是很深,另外一方面那個論壇確實更強大。參考:
python raw-input odd behavior with accents containing strings
它是將終端的輸入編碼經過decode轉換成unicode編碼
key = raw_input("Please input a key: ").decode(sys.stdin.encoding)segmentfault
此時你的爬蟲僅僅是能從raw_input中輸入進行處理或者定義unicode的字符串進行定向爬取,可是若是關鍵詞不少就須要經過讀取文件來實現。以下圖所示,是我"Python爬取百度InfoBox"這篇文章。一樣,你會遇到各類中文亂碼問題須要處理。windows
舉個簡單例子:經過Selenium爬取百度百科Summary第一段。數組
# coding=utf-8 import sys import os import urllib import time from selenium import webdriver from selenium.webdriver.common.keys import Keys import selenium.webdriver.support.ui as ui from selenium.webdriver.common.action_chains import ActionChains #driver = webdriver.PhantomJS(executable_path="G:\phantomjs-1.9.1-windows\phantomjs.exe") driver = webdriver.Firefox() wait = ui.WebDriverWait(driver,10) def getTitle(line,info): print 'Fun: ' + line,type(line) driver.get("http://baike.baidu.com/") elem_inp = driver.find_element_by_xpath("//form[@id='searchForm']/input") elem_inp.send_keys(line) elem_inp.send_keys(Keys.RETURN) elem_value = driver.find_element_by_xpath("//div[@class='lemma-summary']/div[1]").text print 'Summary ',type(elem_value) print elem_value,'\n' info.write(line.encode('utf-8')+'\n'+elem_value.encode('utf-8')+'\n') time.sleep(5) def main(): source = open("E:\\Baidu.txt",'r') info = open("E:\\BaiduSpider.txt",'w') for line in source: line = line.rstrip('\n') print 'Main: ' + line,type(line) line = unicode(line,"utf-8") getTitle(line,info) else: info.close() main()
其中TXT一般默認爲ANSI編碼,代碼步驟:
1.我先把Baidu.txt修改成utf-8編碼,同時讀入經過unicode(line,'utf-8')將str轉換爲unicode編碼;
2.Selenium先經過打開百度百科,在輸入關鍵詞"北京故宮"進行搜索,經過find_element_by_xpath爬取"故宮"的summary第一段內容,並且編碼方式爲unicode;
3.最後文件寫操做,經過line.encode('utf-8')將unicode轉換成utf-8,不然會報錯UnicodeDecodeError: 'ascii'。
總之過程知足:編碼=》Unicode=》處理=》utf-8或gbk瀏覽器
因爲建立txt文件時默認是ascii格式,而文字爲'utf-8'格式時會報錯。固然你也能夠經過CODECS方法建立制定格式文件。
codes是COder/DECoder的首字母組合。它定義了文本跟二進制值的轉換方式,跟ASCII那種用一個字節把字符轉換成數字的方式不一樣,Unicode用的是多字節。這也致使了Unicode支持多種不一樣的編碼方式。codes支持的四種編碼方式包括:ASCII、ISO 8859-1/Latin-一、UTF-8和UTF-16。
import codecs #用codecs提供的open方法來指定打開的文件的語言編碼,它會在讀取的時候自動轉換爲內部unicode info = codecs.open(baiduFile,'w','utf-8') #該方法不是io故換行是'\r\n' info.writelines(key.text+":"+elem_dic[key].text+'\r\n')
PS: 該部分主要參考書籍《Python核心編程(第二版)》做者Wesley J.Chun
什麼是Unicode
Unicode字符串聲明經過字母"u",它用來將標準字符串或者是包含Unicode字符的字符串轉換成徹底的Unicode字符串對象。Python1.6起引進Unicode字符串支持,是用來在多種雙字節字符的格式、編碼進行轉換的。
Unicode是計算機支持這個星球上多種語言的祕密武器。在Unicode以前,用的都是ASCII碼,每一個英文字符都是以7位二進制數的方式存儲在計算機內,其範圍是32~126。當用戶在文件中鍵入A時,計算機會把A的ASCII碼值65寫入磁盤,而後當計算機讀取該文件時,它會首先把65轉換成字符A再顯示到屏幕上。
可是它的缺點也很明顯:對於成千上萬的字符來講,ASCII實在太少。而Unicode經過使用一個或多個字節來表示一個字符的方法,能夠表示超過90,000個字符。
>>> s1 = "中文" >>> s1 '\xd6\xd0\xce\xc4' >>> print s1,type(s1) 中文 <type 'str'> >>> s2 = u"中文" >>> s2 u'\xd6\xd0\xce\xc4' >>> print s2,type(s2) ÖÐÎÄ <type 'unicode'> >>>
前面添加'u'聲明爲Unicode字符串,但它實際的編碼並無改變。
編碼轉碼
Unicode支持多種編碼格式,這爲程序員帶來了額外的負擔,每當你向一個文件寫入字符串的時候,你必須定義一個編碼(encoding參數)用於把對應的Unicode內容轉換成你定義的格式,經過encode()函數實現;相應地,當咱們從這個文件讀取數據時,必須"解碼"該文件,使之成爲相應的Unicode字符串對象。
str1.decode('gb2312') 解碼錶示將gb2312編碼字符串轉換成unicode編碼
str2.encode('gb2312') 編碼表示將unicode編碼的字符串轉換成gb2312編碼
>>> s = '中文' >>> s '\xd6\xd0\xce\xc4' >>> print s,type(s) 中文 <type 'str'> >>> s.decode('gb2312') u'\u4e2d\u6587' >>> print s.decode('gb2312'),type(s.decode('gb2312')) 中文 <type 'unicode'> >>> len(s) 4 >>> len(s.decode('gb2312')) 2 >>> t = u'中文' >>> t u'\xd6\xd0\xce\xc4' >>> len(t) 4 >>> print t,type(t) ÖÐÎÄ <type 'unicode'> >>>
前綴'u'表示字符串是一個Unicode串,僅僅是一個聲明。
Unicode實際應用
1.程序中出現字符串時必定要加個前綴u
2.不要用str()函數,而是用unicode()代替
3.不要用過期的string模塊——若是給它的是非ASCII字符,它會把一切搞砸
4.不到必要時不要再程序裏面編解碼Unicode字符。只在你要寫入文件或數據庫或網絡時,才調用encode()函數;相應地,只在須要把數據讀回來時才調用decode()函數
5.因爲pickle模塊只支持ASCII字符串,儘可能避免基於文本的pickle操做
6.假設構建一個用數據庫來讀寫Unicode數據的Web應用,必須保持如下對Unicode的支持
· 數據庫服務器(MySQL、PostgreSQL、SQL Server等)
· 數據庫適配器(MySQLLdb等)
· Web開發框架(mod_python、cgi、Zope、Django等)
數據庫方面確保每張表都用UTF-8編碼,適配器若是不支持Unicode如MySQLdb,則必須在connect()方法裏面用一個特殊的關鍵字use_unicode來確保獲得的查詢結果是Unicode字符串。mod_python開啓對Unicode的支持便可,只要在request對象裏面把text-encoding設爲「utf-8」就OK了。同時瀏覽器也注意下。
總結:使用應用程序徹底支持Unicode,兼容其餘的語言自己就是一個工程。它須要詳細的考慮、計劃。全部涉及的軟件、系統都須要檢查,包括Python的標準庫和其餘要用到的第三方擴展模塊。你甚至須要組件一個經驗豐富的團隊來專門負責國家化(I18N)問題。
源自:http://xianglong.me/article/learn-python-1-chinese-encoding/
結合我遇到的兩個問題,概括瞭如下幾點。常見中文編碼問題解決方法包括:
1.遵循PEP0263原則,聲明編碼格式
在PEP 0263--Defining Python Source Code Encodings中提出了對Python編碼問題的最基本的解決方法:在Python源碼文件中聲明編碼格式,最多見的聲明方式:
#!/usr/bin/python # -*- coding: <encoding name> -*-
根據這個聲明,Python會嘗試將文件中的字符編碼轉爲encoding編碼,它能夠是任意一種Python支持的格式,通常都會使用utf-8\gbk的編碼格式。而且它儘量的將指定地編碼直接寫成Unicode文本。
注意,coding:encoding只是告訴Python文件使用了encoding格式的編碼,可是編輯器可能會以本身的方式存儲.py文件,所以最後文件保存的時候還須要編碼中選指定的ecoding才行。
2.字符串變量賦值時添加前綴u,使用 u'中文' 替代 '中文'
str1 = '中文' str2 = u'中文'
Python中有以上兩種聲明字符串變量的方式,它們的主要區別是編碼格式的不一樣,其中tr1的編碼格式和Python文件聲明的編碼格式一致,而str2的編碼格式則是Unicode。
若是你要聲明的字符串變量中存在非ASCII的字符,那麼最好使用str2的聲明格式,這樣你就能夠不須要執行decode,直接對字符串進行操做,能夠避免一些出現異常的狀況。
3.重置默認編碼
Python中出現這麼多編碼問題的根本緣由是Python 2.x的默認編碼格式是ASCII,因此你也能夠經過如下的方式修改默認的編碼格式:sys.getdefaultencoding()默認是'ascii'編碼。
#設置編碼utf-8 import sys reload(sys) sys.setdefaultencoding('utf-8') #顯示當前默認編碼方式 print sys.getdefaultencoding()
這種方法是能夠解決部分編碼問題,可是同時也會引入不少其餘問題,得不償失,不建議使用這種方式。
其原理: 首先, 這個就是Python語言自己的問題。由於在Python 2.x的語法中, 默認的str並非真正意義上咱們理解的字符串, 而是一個byte數組, 或者能夠理解成一個純ascii碼字符組成的字符串, 與Python 3中的bytes類型的變量對應; 而真正意義上通用的字符串則是unicode類型的變量, 它則與Python 3中的str變量對應。原本應該用做byte數組的類型, 卻被用來作字符串用, 這種看似奇葩的設定是Python 2一直被人詬病的東西, 不過也沒有辦法, 爲了與以前的程序保持兼容.。
在Python 2中做爲兩種字符串類型, str與unicode之間就須要各類轉換的方式。首先是一種顯式轉換的方式, 就是encode和decode兩種方法。在這裏這兩貨的意思很容易被搞反, 科學的調用方式是:
str --- decode方法 ---> unicode
unicode --- encode方法 ---> str
4.終極原則:decode early, unicode everywhere, encode late
Decode early:儘早decode, 將文件中的內容轉化成unicode再進行下一步處理
Unicode everywhere:程序內部處理都用unicode,好比字符串拼接、替換、比較等操做
Encode late:最後encode回所需的encoding, 例如把最終結果寫進結果文件
按照這個原則處理Python的字符串,基本上能夠解決全部的編碼問題(只要你的代碼和Python環境沒有問題)。前面講述的兩個問題解決實質也是這樣,只是有些取巧便可。
5.使用decode().encode()方法
網頁採集時,代碼指定#coding:utf-8,若是網頁的編碼爲gbk須要這樣處理:
html = html.decode('gbk').encode('utf-8')
6.輸入變量raw_input中文編碼
將終端的輸入編碼str經過decode轉換成unicode編碼,再使用unicode處理:
key = raw_input("Please input a key: ").decode(sys.stdin.encoding)
7.文件讀寫操做
因爲默認的txt文件爲ANSI編碼,讀取時經過unicode轉碼,通過「編碼=》Unicode=》處理=》utf-8或gbk 」順序便可。同時文件輸出時encode('utf-8')轉換txt爲UTF-8格式。終極代碼:
info = codecs.open(baiduFile,'w','utf-8')
8.升級Python 2.x到3.x
最後一個方法:升級Python 2.x,使用Python 3.x版本。這樣說主要是爲了吐槽Python 2.x的編碼設計問題。固然,升級到Python 3.x確定能夠解決大部分由於編碼產生的異常問題。畢竟Python 3.x版本對字符串這部分仍是作了至關大的改進的。
在Python 3.0以後的版本中,全部的字符串都是使用Unicode編碼的字符串序列,同時還有如下幾個改進:
· 默認編碼格式改成unicode
· 全部的Python內置模塊都支持unicode
· 再也不支持u'中文'的語法格式
因此,對於Python 3.x來講,編碼問題已經再也不是個大的問題,基本上不多遇到上述的幾個異常。
總結
最後但願文章對你有所幫助,尤爲是你恰好遇到這個問題的,因爲是結合最近作的東西,因此文章比較雜亂,但若是你恰好須要,確實能解決你的問題的。
紀伯倫曾說過:「你沒法同時擁有青春和關於青春的知識;由於青春忙於生計,沒有餘暇去求知;而知識忙於尋求自我,沒法享受生活。」
一樣如今找工做的我,沒法在擁有紮實基礎知識的同時又兼顧深度的項目理解,但我更傾向於分享知識,由於它就是尋求自我,就是享受生活,就是編程之樂~
(By:Eastmount 2015-10-1 晚上11點 http://blog.csdn.net/eastmount/)