【問題解答】python解析網頁源代碼返回亂碼問題 | 在路上
【問題】
python解析網頁源代碼返回亂碼問題
代碼:
輸出:
直接print中文是可以的,注釋的中文也不會亂碼,就解析網頁亂碼 求有用解決方案!!
【問題解答】
從問題現象到問題本質,一點點幫你分析為何如何,以及如何解決問題1.首先,根據之前的Python的IDE的經驗:
【整理】各種Python的IDE(集成開發環境)的總結和對比
從上述截圖,基本上,可以推測出來,其用的是:
【記錄】使用Python的IDE:Eclipse+PyDev
2.所以,為了完全重現其問題,專門去PyDev中建立了對應的項目。
代碼為:
# -*- coding: utf-8 -*- """Created on Oct 18, 2013@author: CLi"""from urllib import urlopendef getHtml(url): page = urlopen(url) html = page.read() page.close() return htmlif __name__ == "__main__": url = "http://www.sina.com.cn" html = getHtml(url) print html
然後,輸出,果然和其一樣:
3.對於此現象的原因:
(根據:詳解抓取網站,模擬登陸,抓取動態網頁的原理和實現(Python,C#等)的:【整理】關於HTML網頁源碼的字元編碼(charset)格式(GB2312,GBK,UTF-8,ISO8859-1等)的解釋)
先去確認了所要處理的網頁:
http://www.sina.com.cn/
中有:
<meta http-equiv="Content-type" content="text/html; charset=gb2312" />
(再根據:字元編碼簡明教程)
可知,此處的代碼處理中,如果需要解碼為Unicode,最好用GBK。
而此處得到的sina網頁的字元串,是GB2312
對應的,在Eclipse+PyDev中的console中輸出,結果卻亂碼了。
則可以確定:
Eclipse+PyDev的console中,所用字元編碼不是GB2312(也不是GBK或GB18030)
猜測:
估計是用的,相對常見的,UTF-8
4.但是不管其用什麼編碼,將Unicode的字元串輸出,則肯定應該是可以的:
5.為了驗證上面的PyDev的console是UTF-8的猜測,所以去用代碼:
# -*- coding: utf-8 -*- """Created on Oct 18, 2013@author: CLi"""from urllib import urlopendef getHtml(url): page = urlopen(url) html = page.read() page.close() #uniCodehtml = html.decode("GBK") #return uniCodehtml uniCodehtml = html.decode("GBK") utf8html = uniCodehtml.encode("UTF-8") return utf8htmlif __name__ == "__main__": url = "http://www.sina.com.cn" html = getHtml(url) print html
結果輸出果然是所預測的,正常的:
6.接下來,就很明顯了:
需要搞清楚,為何此處:
Eclipse+PyDev的Console中,
(不是我們之前所以為的,調用了Windows的cmd,以為是GBK編碼)
卻是UTF-8編碼
然後參考:
Printing Unicode in eclipse Pydev console and in Idle
而去看看:
結果在Eclipse的Window->Preferences中,找了半天,都沒有找到關於console的encoding方面的設置。
最後是在:
選擇你的PyDev項目->Run->Run Configuration-> Python Run ->選擇你當前的PyDev項目->Common->Encoding
才看到:
當前的,你的PyDev項目,運行期間,所用的console的編碼是utf-8
7.對應的,去改為GBK:
然後再點擊Run,結果,用之前,將Unicode故意轉為UTF-8的話,則輸出此時就是,所預料到的,亂碼了:
對應的,再去用,最原始的代碼,直接輸出(得到的GB2312的sina網頁),不做任何轉換的代碼:
8.由此,可以完全明白原因了:
(1)從sina獲得的html網頁,這個字元串,本身的字元編碼為:GB2312
(2)直接print輸出到,Eclipse+PyDev,中的console時,由於console默認是UTF-8編碼,所以:
將GB2312的sina的html網頁字元串輸出到UTF-8的Eclipse+PyDev的console中
必然導致亂碼。
而由於前面多次的嘗試可知,解決辦法有多種:
(1)代碼不改:
from urllib import urlopendef getHtml(url): page = urlopen(url) html = page.read() page.close() return htmlif __name__ == "__main__": url = "http://www.sina.com.cn" html = getHtml(url) print html
但是去改,Eclipse+PyDev的console的字元編碼,從UTF-8改為GBK(或GB2312,或GB18030):
選擇你的PyDev項目->Run->Run Configuration-> Python Run ->選擇你當前的PyDev項目->Common->Encoding->改為GBK
(2)不改Eclipse+PyDev的console的字元編碼,仍保持舊的UTF-8的配置,但是去改代碼:
from urllib import urlopendef getHtml(url): page = urlopen(url) html = page.read() page.close() uniCodehtml = html.decode("GBK") return uniCodehtmlif __name__ == "__main__": url = "http://www.sina.com.cn" html = getHtml(url) print html
即,獲得GB2312的html後,去decode為Unicode,然後直接輸出Unicode字元串到UTF-8的Eclipse+PyDev的console中,
則比如也是可以正常顯示無亂碼的
(內部會自動將輸出的Unicode轉換為當前console的UTF-8編碼的字元串的)
(3)基於上面那種方式,(不改Eclipse+PyDev的console的字元編碼,仍保持舊的UTF-8的配置),但是在轉換為Unicode後,再故意轉為輸出目標(此處的Eclipse+PyDev的console)的編碼,即UTF-8:
from urllib import urlopendef getHtml(url): page = urlopen(url) html = page.read() page.close() #uniCodehtml = html.decode("GBK") #return uniCodehtml uniCodehtml = html.decode("GBK") utf8html = uniCodehtml.encode("UTF-8") return utf8htmlif __name__ == "__main__": url = "http://www.sina.com.cn" html = getHtml(url) print html
也是可以保證輸出的網頁是正常的,中文不是亂碼。
關於返回的網頁是否要解壓縮另外,有人提到的:
(以為此處獲得的網頁是壓縮的)
所以需要對於抓取回來的網頁進行解壓縮
此處實際結果證明了:
是不需要的。
其背後的原理和邏輯是:
(正常情況下)只有當你抓取網頁所提交的請求,包含了說你支持(即需要)返回壓縮的網頁
(人家伺服器)才返回對應的壓縮的網頁
詳見:
【總結】靜態網頁抓取,動態網頁抓取,模擬登陸的注意事項和心得
中的:「返回的html內容是二進位的亂碼」
進一步分析問題的現象和背後的本質至此,上面的所有分析,還只是:
和解壓代碼無關,和字元串編碼有關
但是:
此處,才注意到,提問者提問所截的圖
看起來,還的確是:
列印出來的亂碼
像是二進位級別的字元串亂碼
而非之前所討論的,中文的亂碼
此刻,猜測問題最大的可能性是:
提問者,沒有給出背後相關的其所用的所有的代碼
估計其代碼,除了此處截圖給出的代碼之外
還是在別處,用到了對應的:
【總結】靜態網頁抓取,動態網頁抓取,模擬登陸的注意事項和心得
中的:「返回的html內容是二進位的亂碼」
所提到的:
添加了對應的頭信息,其中包含了:
Accept-Encoding設置了gzip,deflate
從而導致:
此處,返回的網頁,直接列印出現二進位那種的亂碼
(而不是字元編碼設置錯誤的,只會導致中文異常的那種亂碼)
所以:
無論哪種情況,此種,二進位亂碼
的解決辦法是:
核心相關代碼:
#print "---before unzip, len(respHtml)=",len(respHtml);respInfo = resp.info(); # Server: nginx/1.0.8# Date: Sun, 08 Apr 2012 12:30:35 GMT# Content-Type: text/html# Transfer-Encoding: chunked# Connection: close# Vary: Accept-Encoding# ...# Content-Encoding: gzip # sometime, the request use gzip,deflate, but actually returned is un-gzip html# -> response info not include above "Content-Encoding: gzip"# eg: http://blog.sina.com.cn/s/comment_730793bf010144j7_3.html# -> so here only decode when it is indeed is gziped data #Content-Encoding: deflateif("Content-Encoding" in respInfo): if("gzip" == respInfo["Content-Encoding"]): respHtml = zlib.decompress(respHtml, 16+zlib.MAX_WBITS); elif("deflate" == respInfo["Content-Encoding"]): respHtml = zlib.decompress(respHtml, -zlib.MAX_WBITS);
更詳細的解釋,參見:
【總結】靜態網頁抓取,動態網頁抓取,模擬登陸的注意事項和心得
中的:「返回的html內容是二進位的亂碼」
對應的,很明顯,上面的核心代碼,
本來就無需,無法,直接加入到,此處提問者所貼出的那段代碼的:
因為:
其所給出的代碼,根本就不應該存在此問題才對。
更加,功能豐富的,用於抓取網頁的函數,其實我早就幫你們封裝好了,只是你們不知道,沒去用而已。
感興趣的,自己去看:
獲得Url地址的響應:getUrlResponse
和:
獲得Url返回的HTML網頁(源碼)內容:getUrlRespHtml
即可。
【總結】
對於:
Eclipse+PyDev的console,默認是UTF-8,而不是GBK
導致讓不熟悉的人誤以為,輸出GBK(或Unicode)的字元串,
會像輸出到GBK的Windows的cmd中一樣:
中文不會亂碼
如此細節:
建議:
1.對於字元編碼相關知識本身不熟悉,可參考:
複雜教程:
字元編碼詳解
簡明教程:
字元編碼簡明教程
2.不熟悉Python的,自己去看:
python初級教程:入門詳解
3.對於初學者,還是建議先習慣和熟悉普通文件加上cmd去開發Python,熟悉了後,再去用各種IDE。
詳細解釋見:
總結:到底使用哪種環境去開發Python
4.然後對於Python字元編碼方面,不了解的話,再去看:
Python專題教程:字元串和字元編碼
5.字元編碼書序了,Python也熟悉了,Python中的字元編碼也熟悉了之後,對於PyDev不熟悉,可參考:
【整理】各種Python的IDE(集成開發環境)的總結和對比
中的:
【記錄】使用Python的IDE:Eclipse+PyDev
最終:
等所有相關知識都熟悉了後:
你:
愛用啥Python的IDE,就用啥;
愛怎麼折騰IDE的配置,就怎麼折騰;
愛怎麼寫代碼,去抓取網頁,處理網頁,就如何折騰,反正都可以搞定
無論幹啥,都是:
先要知道背景知識
然後:
折騰東西,出錯後,也才知道,錯誤的原因到底是啥,以及具體如何解決問題。
推薦閱讀:
※解答2歲寶寶英語啟蒙的困惑
※捐精有什麼神秘之處,丁香醫生給你一一解答
※佛學問答類編06 李炳南老居士解答
※【香港旅遊】關於港澳通行證的若干問題解答,必收藏!
※佛學問答類編(凈土第十二).(十).李炳南老居士解答