在寫爬蟲爬取網(wǎng)頁信息時,發(fā)生了以下錯誤:
UnicodeEncodeError: 'gbk' codec can't encode character '\ufffd'
意思大致是Unicode編碼錯誤,gbk編解碼器不能編碼\ufffd
字符。
爬蟲程序爬取的是課程信息,包含中文。使用requests庫訪問網(wǎng)頁,使用BeautifulSoup庫解析網(wǎng)頁,用get_text()
方法獲取標簽內(nèi)的文本信息。
python版本為3.5,在cmd控制臺中運行python腳本。
代碼大致如下:
import requestsfrom bs4 import BeautifulSoupr = requests.get(url,cookies=cookies,headers=headers)soup = BeautifulSoup(r.text,"lxml")lesson_data_list = soup.find_all(id="xjs_table")[0].find_all("tr")[1:]for lesson_data in lesson_data_list: td = lesson_data.find_all("td") name = td[0].get_text() credit = td[1].get_text() teacher = td[2].get_text() time = td[5].get_text() total = td[11].get_text() print(name,credit,teacher,time,total)
cmd默認編碼是GBK,字符\ufffd
不能編碼為GBK。
查閱Unicode編碼表或者使用Python自帶的集成開發(fā)環(huán)境IDLE(可以輸出),可知\ufffd
其實是?字符。
Python3的字符串以Unicode編碼,也就是解析網(wǎng)頁時,需要將字符串從網(wǎng)頁原來的編碼格式轉(zhuǎn)化為Unicode編碼格式。
出現(xiàn)?字符的原因:從某編碼向Unicode編碼轉(zhuǎn)化時,如果沒有對應的字符,得到的將是Unicode的代碼\ufffd
,也就是?字符。
在寫爬蟲解析網(wǎng)頁時出現(xiàn)?字符,往往是因為沒有注意原網(wǎng)頁的編碼格式,全按照默認編碼UTF-8轉(zhuǎn)化,導致有的字符轉(zhuǎn)化失敗。
在本問題中,是由于在網(wǎng)頁上包含珺
、玥
這樣的中文,由于珺
、玥
是生僻字沒有相應的UTF-8編碼,所以以默認UTF-8編碼格式轉(zhuǎn)化為Unicode時,沒有對應的字符,轉(zhuǎn)化出錯,得到\ufffd
。又因為在cmd運行,因此報錯UnicodeEncodeError: 'gbk' codec can't encode character '\ufffd'
。
寫爬蟲解析網(wǎng)頁時,要注意原網(wǎng)頁的編碼格式和壓縮格式(Gzip等)
查看原網(wǎng)頁的編碼格式,為’gb2312’。
r = requests.get(url,cookies=cookies,headers=headers)# 指定網(wǎng)頁的編碼格式r.encoding = 'gbk'# r.encoding = 'gb2312' 仍然會報錯soup = BeautifulSoup(r.text,"lxml")
最后再次在cmd運行代碼,珺
、玥
這樣的中文也成功顯示。
注意:本解決辦法適用于與本問題描述完全相同的問題,因為可能是相同的錯誤原因?qū)е碌摹F渌鼒箦e信息可能與本問題的情況不同,比如報錯的為‘gbk’ codec can’t encode character ‘\xa0’,爬蟲在寫入文件時報錯。
以下是問題的逐步分析解決過程。
對于Unicode字符,需要print出來的話,由于本地系統(tǒng)是Windows中的cmd,默認codepage是CP936,即GBK的編碼,所以python解釋器需要先將上述的Unicode字符編碼為GBK,然后再在cmd中顯示出來。但是由于該Unicode字符串中包含一些GBK中無法顯示的字符,導致此時提示'gbk' codec can’t encode
的錯誤的。
分析:這個解釋確實符合報錯信息'gbk' codec can't encode character '\ufffd'
的意思。
驗證:編寫python代碼
print('\ufffd')
在cmd下運行,出現(xiàn)相同的報錯信息'gbk' codec can't encode character '\ufffd'
解決辦法1:改變標準輸出的默認編碼
在程序的開始加入以下代碼
import sysimport iosys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030')
由于我輸出的包含中文,所以使encoding='gb18030'
,這段代碼的作用就是把標準輸出的默認編碼修改為gb18030,也就是與cmd顯示編碼GBK相同。
效果:運行原來的爬蟲代碼后,沒有了報錯,但原先報錯的輸出位置顯示為??
,中文仍沒有正確顯示。
解決辦法2:輸出時忽略無法編碼的字符
在對Unicode字符編碼時,添加ignore參數(shù),可以忽略無法編碼的字符
print 你的字符串.encode(“GBK“, ‘ignore’).decode
這句代碼的作用為,把字符串以GBK編碼,并忽略無法編碼的字符,再以GBK解碼,再輸出。
效果:在解決辦法1的效果基礎上,??
不再顯示,但中文仍沒有正確顯示。
\ufffd
本身就沒有對應的Unicode編碼,所以要在cmd中不報錯輸出,只能把輸出編碼改為GBK或者在輸出時忽略無法編碼的字符,這樣就算可以不報錯輸出,也不能正確顯示\ufffd
字符。cmd的顯示編碼是GBK,要想正確顯示\ufffd
字符,就要使用支持多種輸出編碼的運行環(huán)境。Python自帶的集成開發(fā)環(huán)境IDLE就對GBK、UTF-8、Unicode都支持。其實,Pycharm、jupyter notebook、sublimeREPL配置的python運行環(huán)境也支持多種輸出編碼。
IDLE打開方法:在python的安裝目錄下,打開lib/idlelib/idle.bat
,就進入了IDLE環(huán)境
運行代碼
print('\ufffd')
輸出為: ?
\ufffd
字符已經(jīng)正確顯示了。 以上分析的原因和解決辦法都是從最后顯示輸出的角度去考慮的,雖然一些方法不再報錯,但仍然顯示不正確,并沒有真正解決。然后在IDLE中正確顯示了字符,證明了\ufffd
就是?字符。
從以上解決辦法可以進一步驗證和推測一些問題:
問題
本來應該顯示的漢字是什么
?是從哪來的
推測
對照原網(wǎng)頁,發(fā)現(xiàn)是珺
和玥
字沒有正確顯示,而變成了\fffd
?字符。這說明從一開始解析網(wǎng)頁,珺
和玥
就被解析成了?字符。
驗證
如果從報錯信息找問題,報錯原因是部分Unicode字符不能正確轉(zhuǎn)碼成GBK,所以無法顯示。根據(jù)推測,原網(wǎng)頁上的珺
和玥
字沒有正確顯示,而運行以下代碼:
str = '珺玥'str = str.encode('unicode_escape')print(str)# 輸出 b'\\u73fa\\u73a5'
可知珺
和玥
的Unicode分別為73fa
和73a5
。而不是fffd
。
經(jīng)驗證,print('\u73a5')
是可以輸出的玥
字的。這說明并不是玥
的Unicode轉(zhuǎn)碼為gbk失敗,而是解析玥
得到的Unicode都不正確,變成了\ufffd
?字符。
使用爬蟲 ?
作為關鍵字搜索,就找到了出現(xiàn)?字符的原因:
從某編碼向Unicode編碼轉(zhuǎn)化時,如果沒有對應的字符,得到的將是Unicode的代碼“\uffffd”,也就是?這個字符。這個是你的爬蟲根本不識別原網(wǎng)頁的編碼格式(ASCII或者GB2312等)和壓縮格式(Gzip等),全都無腦轉(zhuǎn)成UTF-8字符串導致的,出現(xiàn)這個字符說明轉(zhuǎn)換失敗,數(shù)據(jù)已經(jīng)丟失了,這個字符本身并沒什么實際意義。
出自鏈接:在處理一些爬下來的網(wǎng)頁時,經(jīng)常發(fā)現(xiàn)會存在?這個字符
在進一步分析后,重新檢查解析過程,顯然是編碼問題。
最后發(fā)現(xiàn)確實是因為沒有注意到網(wǎng)頁編碼問題,網(wǎng)頁編碼是gb2312,而用requests請求的網(wǎng)頁,如果不聲明encoding值,默認是用UTF-8解析的。UTF-8編碼支持大部分中文字,但不支持珺
、玥
這樣的生僻字,這樣轉(zhuǎn)成UTF-8就轉(zhuǎn)化失敗。
所以要按照gb2312編碼向Unicode編碼轉(zhuǎn)化:
r = requests.get(url,cookies=cookies,headers=headers)# 指定網(wǎng)頁的編碼格式r.encoding = 'gbk'# r.encoding = 'gb2312' 仍然會報錯soup = BeautifulSoup(r.text,"lxml")
最后再次在cmd運行代碼,珺
、玥
這樣的中文也成功顯示。
最終總結,是由于在網(wǎng)頁上包含珺
、玥
這樣的中文,由于珺
、玥
是生僻字沒有相應的UTF-8編碼,所以以默認UTF-8編碼格式轉(zhuǎn)化為Unicode時,沒有對應的字符,轉(zhuǎn)化出錯,得到\ufffd
。又因為在cmd運行,因此報錯UnicodeEncodeError: 'gbk' codec can't encode character '\ufffd'
。