這兩天寫了個監測網頁的爬蟲,作用是跟蹤一個網頁的變化,但運行了一晚出現了一個問題。。。。希望大家不吝賜教!
我用的是python3,錯誤在對html response的decode時拋出,代碼原樣為:
response = urllib.urlopen(dsturl) content = response.read().decode('utf-8')
拋出錯誤為
File "./unxingCrawler_p3.py", line 50, in getNewPhones content = response.read().decode() UnicodeDecodeError: 'utf8' codec can't decode byte 0xb2 in position 24137: invalid start byte
之前運行都沒問題,經過一晚上就出現了。。。。最不明白的是在它聲明為utf-8編碼的網頁中為什么會出現utf-8無法解析的字符?
后來經過熱心網友的提醒,才發現需要使用
decode('utf-8', 'ignore')
為了徹底鬧明白python的編碼問題,特分享下文,希望對大家熟悉python的編碼問題帶來些幫助
1.從字節說起:
一個字節包括八個比特位,每個比特位表示0或1,一個字節即可表示從00000000到11111111共2^8=256個數字。一個ASCII編碼使用一個字節(除去字節的最高位作為作奇偶校驗位),ASCII編碼實際使用一個字節中的7個比特位來表示字符,共可表示2^7=128個字符。比如ASCII編碼中的01000001(即十進制的65)表示字符'A',01000001加上32之后的01100001(即十進制的97)表示字符'a'?,F在打開Python,調用chr和ord函數,我們可以看到Python為我們對ASCII編碼進行了轉換。如圖
第一個00000000表示空字符,因此ASCII編碼實際上只包括了 字母、標點符號、特殊符號等共127個字符。因為ASCII是在美國出生的,對于由字母組成單詞進而用單詞表達的英文來說也是夠了。但是中國人、日本人、 韓國人等其他語言的人不服了。中文是一個字一個字,ASCII編碼用上了渾身解數256個字符都不夠用。
因此后來出現了Unicode編碼。Unicode編碼通常由兩個字節組成,共表示256*256個字符,即所謂的UCS-2。某些偏僻字還會用到四個字節,即所謂的UCS-4。也就是說Unicode標準也還在發展。但UCS-4出現的比較少,我們先記?。?最原始的ASCII編碼使用一個字節編碼,但由于語言差異字符眾多,人們用上了兩個字節,出現了統一的、囊括多國語言的Unicode編碼。
在Unicode中,原本ASCII中的127個字符只需在前面補一個全零的字節即可,比如前文談到的字符‘a':01100001,在Unicode中變成了00000000?01100001。不久,美國人不開心了,吃上了世界民族之林的大鍋飯,原本只需一個字節就能傳輸的英文現在變成兩個字節,非常浪費存儲空間和傳輸速度。
人們再發揮聰明才智,于是出現了UTF-8編碼。因為針對的是空間浪費問題,因此這種 UTF-8編碼是可變長短的 ,從英文字母的一個字節,到中文的通常的三個字節,再到某些生僻字的六個字節。解決了空間問題,UTF-8編碼還有一個神奇的附加功能,那就是兼容了老大哥的ASCII編碼。一些老古董軟件現在在UTF-8編碼中可以繼續工作。
注意除了英文字母相同,漢字在Unicode編碼和UTF-8編碼中通常是不同的。比如?漢字的‘中'字在Unicode中是01001110 00101101,而在UTF-8編碼中是11100100 10111000 10101101。
我們祖國母親自然也有自己的一套標準。那就是GB2312和GBK。當然現在挺少看到。通常都是直接使用UTF-8。
2.Python3中的默認編碼
Python3中默認是UTF-8,我們通過以下代碼:
import sys sys.getdefaultencoding()
可查看Python3的默認編碼。?
3.Python3中的?encode和decode
Python3中字符編碼經常會使用到decode和encode函數。特別是在抓取網頁中,這兩個函數用的熟練非常有好處。encode的作用,使我們看到的直觀的字符轉換成計算機內的字節形式。decode剛好相反,把字節形式的字符轉換成我們看的懂的、直觀的、“人模人樣”的形式。
\x表示后面是十六進制, \xe4\xb8\xad即是二進制的 11100100 10111000 10101101。也就是說漢字‘中'encode成字節形式,是 11100100 10111000 10101101。同理,我們拿 11100100 10111000 10101101也就是 \xe4\xb8\xad來decode回來,就是漢字‘中'。完整的應該是 b'\xe4\xb8\xad',在Python3中, 以字節形式表示的字符串則必須加上 前綴b,也就是寫成上文的b'xxxx'形式。
前文說的Python3的默認編碼是UTF-8,所以我們可以看到,Python處理這些字符的時候是以UTF-8來處理的。因此從上圖可以看到,就算我們通過encode('utf-8')特意把字符encode為UTF-8編碼,出來的結果還是相同:b'\xe4\xb8\xad'。
明白了這一點,同時我們知道?UTF-8兼容ASCII,我們可以猜想大學時經常背誦的‘A'對應ASCII中的65,在這里是不是也能正確的decode出來呢。十進制的65轉換成十六進制是41,我們嘗試下:
b'\x41'.decode()
結果如下。果然是字符‘A'
4.Python3中的?編碼轉換
據說字符在計算機的內存中統一是以Unicode編碼的。只有在字符要被寫進文件、存進硬盤或者從服務器發送至客戶端(例如網頁前端的代碼)時會變成utf-8。但其實我比較關心怎么把這些字符以Unicode的字節形式表現出來,露出它在內存中的廬山正面目的。這里有個照妖鏡:
xxxx.encode/decode('unicode-escape')
b'\\u4e2d'還是b'\u4e2d,一個斜杠貌似沒影響。同時可以 發現在shell窗口中,直接輸 '\u4e2d'和輸入b '\u4e2d'.decode('unicode-escape')是相同的,都會打印出漢字‘中', 反而是 '\u4e2d'.decode('unicode-escape')會報錯。說明 說明Python3不僅支持Unicode,而且一個‘\uxxxx'格式的 Unicode字符 可被辨識且被等價于str類型。
如果我們知道一個Unicode字節碼,怎么變成UTF-8的字節碼呢。懂了以上這些,現在我們就有思路了,先decode,再encode。代碼如下:
?xxx.decode('unicode-escape').encode()
?最后的擴展
還記得剛剛那個ord嗎。時代變遷,老大哥ASCII被人合并,但ord還是有用武之地。試試ord('中'),輸出結果是20013。20013是什么呢,我們再試試hex(ord('中')),輸出結果是'0x4e2d',也就是20013是我們在上文見面了無數次的x4e2d的十進制值。這里說下hex,是用來轉換成十六進制的函數,學過單片機的人對hex肯定不會陌生。
最后的擴展,在網上看到的他人的問題。我們寫下類似于'\u4e2d'的字符,Python3知道我們想表達什么。但是讓Python讀取某個文件的時候出現了'\u4e2d',是不是計算機就不認識它了呢?后來下文有人給出了答案。如下:
import codecs file = codecs.open( "a.txt", "r", "unicode-escape" ) u = file.read() print(u)
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
