1. 程式人生 > >python3中使用requests庫出現的編碼問題

python3中使用requests庫出現的編碼問題

最近在使用python完成爬蟲任務時遇到了編碼問題,查看了很多資料,現總結一些編碼的知識。

1、編碼解碼

1、1 字元和位元組

“位元組”是一個8位的物理存貯單元。而“字元”則是一個文化相關的符號。

字元是人類能夠識別的符號,而這些符號要儲存到計算的儲存中就需要用計算機能夠識別的位元組來表示。一個字元往往有多種表示方法,不同的表示方法會使用不同的位元組數。這裡所說的不同的表示方法就是指字元編碼,比如字母A-Z都可以用ASCII碼錶示(佔用一個位元組),也可以用UNICODE表示(佔兩個位元組),還可以用UTF-8表示(佔用一個位元組)。字元編碼的作用就是將人類可識別的字元轉換為機器可識別的位元組碼。

 

1、1 編碼與解碼概念

編碼(encode):字元與二進位制串的對應關係,即:字元(str)→二進位制串(bytes)       

解碼(decode):二進位制串與字元的對應關係,即:二進位制串(bytes)→字元(str)      

編碼實際上是解碼的逆向過程。編碼與解碼有著嚴格的指向性bytes型別只有通過解碼decode轉為str型別,其不可能decode;str型別只能通過encode編碼為bytes型別,str型別不可decode。)

概念搞清就不會出現以下兩種錯誤。

錯誤1
AttributeError: 'bytes' object has no attribute 'encode'
錯誤2
AttributeError: 'str' object has no attribute 'decode'

1、2 常見編碼方式

ASCII :佔1個位元組是最基本的編碼,它定義了0~127對應的字元,包括最基本的英文字母、標點符號。無法表示中文

GB2312 :2個位元組,支援6700+漢字。

GBK :(GB2312的升級版),佔2個位元組,支援21000+漢字。

Unicode : 收錄了各個國家的字元,全球通用,一個漢字佔兩個位元組

在python 3 中字元是以Unicode的形式儲存的,當然這裡所說的儲存是指儲存在計算機記憶體當中,如果是儲存在硬盤裡,Python 3的字元是以bytes形式儲存。也就是說如果要將字元寫入硬碟,就必須對字元進行encode。

Utf-8 :屬於Unicode的一種(為了節省空間)。優先使用1個位元組英文字母),若不滿足則增加位元組。漢字佔3個位元組。最多使用4個。

iso8859-1 通常叫做Latin-1  屬於單位元組編碼,最多能表示的字元範圍是0-255,應用於英文系列。無法表示漢字

1、3 編碼轉換

編碼方式轉化圖

2、python3中編碼

python3中預設使用的是 'utf-8' 編碼

在python命令列中輸入如下即可檢視預設編碼方式:

import sys
sys.getdefaultencoding()

結果顯示如下:

3、requests庫


requests物件的get和post方法都會返回一個Response物件,這個物件裡面存的是伺服器返回的所有資訊,包括響應頭,響應狀態碼等。其中返回的網頁部分會存在Response.content和Response.text兩個物件中。

兩者區別在於,content中間存的是位元組碼,而text中存的是Beautifulsoup根據猜測的編碼方式將content內容解碼成字串。

直接輸出content,會發現前面存在b'這樣的標誌,這是位元組串的標誌,而text是,沒有前面的b,對於純ascii碼,這兩個可以說一模一樣,對於其他的文字,需要正確解碼才能正常顯示。大部分情況建議使用Response.text,因為顯示的是漢字,但有時會顯示亂碼,這時需要用Response.content.decode('utf-8'),中文常用utf-8和GBK,GB2312等。這樣可以手工選擇文字編碼方式.

所以簡而言之,.text是現成的字串,Response.content還要編碼,但是Response.text不是所有時候顯示都正常,這是就需要用Response.content進行手動編碼。

3.1 編碼出現錯誤問題

錯誤源頭:就是reqeusts對當前返回資訊的編碼方式的判斷出現了錯誤。

如果返回response物件中頭部資訊中有charset=utf-8 資訊時,那麼requests庫就能準確知道返回資訊的方式為utf-8編碼形式。

如果返回response物件中頭部資訊中沒有charset=?  資訊時,requests會進行猜測,一般猜測為ISO-8859-1編碼形式。

tips:   

簡單來說 r.encoding  就是requests庫“認為”返回的Response資訊的編碼形式。(不一定正確)

而 r.text 就是將r.content根據r.encoding編碼形式進行解碼.

r.text =  r.content.decode(r.encoding)

 

 example 1

import requests
url = 'http://www.baidu.com'
r = requests.get(url)
print(r.status_code)
print(r.encoding)    #注意此時requests認為r.content編碼形式為ISO-8859-1(推斷錯誤)
#輸出狀態碼為  200
#輸出編碼形式  ISO-8859-1
#檢視網頁返回的headers頭部資訊
print(r.headers)
#輸出 {'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 'Connection': 'Keep-Alive', 'Content-Encoding': 'gzip', 'Content-Type': 'text/html', 'Date': 'Thu, 20 Dec 2018 08:45:21 GMT', 'Last-Modified': 'Mon, 23 Jan 2017 13:28:12 GMT', 'Pragma': 'no-cache', 'Server': 'bfe/1.0.8.18', 'Set-Cookie': 'BDORZ=27315; max-age=86400; domain=.baidu.com; path=/', 'Transfer-Encoding': 'chunked'}  
# 沒有charset內容,此時requests開始進行猜測編碼為ISO-8859-1。

# 到這個地方,會有疑問,requests沒有判斷出來你是怎麼判斷出來的呢?  第一:現在utf-8編碼使用最廣泛,第二:你可以檢視r.content,可見<meta http-equiv=content-type content=text/html;charset=utf-8>這一語句,實際上從這兒可以看出編碼為utf-8.

# 此時用ISO-8859-1去解碼的話,肯定是不可以的,因為網頁實際編碼為utf-8。中文肯定會出現亂碼
print(r.text)
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>ç¾åº¦ä¸ä¸ï¼ä½ å°±ç¥é</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=ç¾åº¦ä¸ä¸ class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>æ°é»</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>å°å¾</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>è§é¢</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>è´´å§</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>ç»å½</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">ç»å½</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">æ´å¤äº§å</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>å
³äºç¾åº¦</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使ç¨ç¾åº¦åå¿
读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>æè§åé¦</a>&nbsp;京ICPè¯030173å·&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>

出現中文亂碼現象。

 

解決辦法:r.encoding = 'utf-8'。輸出成功。

<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新聞</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地圖</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>視訊</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>貼吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登入</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登入</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多產品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>關於百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必讀</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意見反饋</a>&nbsp;京ICP證030173號&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>