這兩天寫了個監測網頁的爬蟲,作用是跟蹤乙個網頁的變化,但執行了一晚出現了乙個問題。。。。希望大家不吝賜教!
我用的是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'。現在開啟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'***x'形式。
前文說的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的位元組形式表現出來,露出它在記憶體中的廬山正面目的。這裡有個照妖鏡:
b'\\u4e2d'還是b'\u4e2d,乙個斜槓貌似沒影響。同時可以 發現在shell視窗中,直接輸 '\u4e2d'和輸入b '\u4e2d'.decode('unicode-escape')是相同的,都會列印出漢字『中', 反而是 '\u4e2d'.decode('unicode-escape')會報錯。說明 說明python3不僅支援unicode,而且乙個『\u***x'格式的 unicode字元 可被辨識且被等價於str型別。
如果我們知道乙個unicode位元組碼,怎麼變成utf-8的位元組碼呢。懂了以上這些,現在我們就有思路了,先decode,再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)
Python3編碼問題
python3最重要的進步之一就是解決了python2中字串與字元編碼的問題。python2字串的缺陷如下 而python3則把系統預設編碼設定為了 utf 8 import sys sys.getdefaultencoding utf 8 之後,文字字元和二進位制資料分別用str和bytes表示。...
python3編碼問題
編碼問題 在python3中只有兩種資料型別 1 str 編碼形式是unicode,unicode任一字元編碼都存在 2 bytes 編碼形式是十六進製制 編碼encoding utf,gbk都只是一種編碼規則,按照各自的規則進行編碼,可以存在多種編碼規則 s hello中國 在記憶體中是以unic...
python3 編碼問題
asci 碼 8 位unicode 至少兩個位元組 utf 8 為了傳輸而設計的編碼方式 用於網路傳輸 或者儲存 python2 使用ascii編碼,不支援中文 python3 使用utf 8 編碼.文字字元和二進位制資料區分得更清晰,分別用 str 和bytes 表示。文字字元全部用 str 型別...