[Python爬虫] 中文编码问题:raw_input输入、文件读取、变量比较等str、unicode、utf-8转换问题
? ? ? ? 中文編碼問題的處理核心都是——保證所有的編碼方式一致即可,包括編譯器、數據庫、瀏覽器編碼方式等,而Python通常的處理流程是將unicode作為中間轉換碼進行過渡。先將待處理字符串用unicode函數以正確的編碼轉換為Unicode碼,在程序中統一用Unicode字符串進行操作;最后輸出時,使用encode方法,將Unicode再轉換為所需的編碼即可,同時保證編輯器服務器編碼方式一致。
? ? ? ? PS:當然Python3除外!這篇文章比較啰嗦,畢竟是在線筆記和體會嘛,望理解~
? ? ? ? 在詳細講解概念之前,先講述我最近遇到的字符編碼的兩個問題及解決。下圖是最常見到幾個問題編碼問題:
參考資料:
? ? ? ??詳解 python 中文編碼與處理
? ? ? ??python字符編碼與解碼unicode、str和中文 'ascii' codec can't decode
? ? ? ??Python的中文編碼問題-segmentfault
? ? ? ? 書籍《Python核心編程(第二版)》和《Python基礎教程(第二版)》
一. raw_input輸入str轉換unicode處理
? ? ? ? 背景:在做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]])
# coding=utf-8 import sysdef getTitle(key,url):#title = driver.find_element_by_xpath()title = u'著名女主播Miss與杰倫直播LOL'print key,type(key)print title,type(title)if key in title:print 'YES'else:print 'NO'key = raw_input("Please input a key: ") print key,type(key) url = 'http://www.baidu.com/' getTitle(key,url)
? ? ? ? 輸出如下圖所示:
? ? ? ? "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)
二. 讀取中文文件亂碼處理
? ? ? ? 此時你的爬蟲僅僅是能從raw_input中輸入進行處理或者定義unicode的字符串進行定向爬取,但是如果關鍵詞很多就需要通過讀取文件來實現。如下圖所示,是我"Python爬取百度InfoBox"這篇文章。同樣,你會遇到各種中文亂碼問題需要處理。
? ? ? ? 舉個簡單例子:通過Selenium爬取百度百科Summary第一段。
? ? ? ? 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-1、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')
三. Unicode詳解
? ? ? ?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個字符。
? ? ? ?編碼轉碼
? ? ? ?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) ?D?? <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源碼文件中聲明編碼格式,最常見的聲明方式:
? ? ? ? 注意,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來說,編碼問題已經不再是個大的問題,基本上很少遇到上述的幾個異常。
總結
? ? ? ? 最后希望文章對你有所幫助,尤其是你剛好遇到這個問題的,由于是結合最近做的東西,所以文章比較雜亂,但如果你剛好需要,確實能解決你的問題的。
? ? ? ??紀伯倫曾說過:“你無法同時擁有青春和關于青春的知識;因為青春忙于生計,沒有余暇去求知;而知識忙于尋求自我,無法享受生活?!?br /> ? ? ? ? 同樣現在找工作的我,無法在擁有扎實基礎知識的同時又兼顧深度的項目理解,但我更傾向于分享知識,因為它就是尋求自我,就是享受生活,就是編程之樂~
總結
以上是生活随笔為你收集整理的[Python爬虫] 中文编码问题:raw_input输入、文件读取、变量比较等str、unicode、utf-8转换问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [Python学习] 专题六.局部变量、
- 下一篇: [python学习] 专题七.网络编程之