這裏記錄的是一個門外漢解決使用 urllib2 抓取網頁時遇到亂碼、崩潰、求助、解決和漲經驗的過程。這類問題,過後看來只是個極小極小的坑,不過居然花去不少時間,也值得記錄一下。過程以下:
目標:html
抓取 http://sports.sina.com.cn/g/premierleague/index.shtmlpython
代碼:服務器
1
2
3
4
5
6
|
# coding: u8
import
urllib2
url
=
"http://sports.sina.com.cn/g/premierleague/index.shtml"
response
=
urllib2.urlopen(url)
html
=
response.read()
print
html
|
輸出:cookie
wױ83’͠L/J
.uVխ[w5;:S煝{7l!Zp8′-y϶=ePUsł;__Zj
::]K챵
eYڕkV%IBUVY」*’)ڤS.
JT>」TTZk+!x*)ld2I,kUUҭ/kXjjkHI U0n2}jUSݲ」>!pj^[LJg’o^=Nqȕ7n|57yy’\ul
j=9T,g/t0ݕ7’^o|v}>8=7흯!tpٹˏgFS?zd~`MuC%U2\ f߉Vqߍ7~2~ɓlE=}M}Xwo}us’>?*zpS:7Oݚ~чb=
HK!sعinQR}@TsY|,#b\d+#yM@qaRTPVNw
?[((tGP,A$O/EXP)oNgA\`Z
4
eL7ȓVn+
ɄeR fT`&WՂbV
f{
j_p@-@[Ib_ͷCZ’!4O1C,کhy b0W(ժZ˨V5-ټX)5{EkvXÝN (PPUCkϫ? j(
V3{Z!LOOP+LP%WPL!\=! @XD8ׯjpT,W+#we~م {CBo@_Y+ijp;^,=(h :NxH|Ar]-|Bkq<
ڻ+}.ܹlt.)cptRXJ4CJЃBv@BXdP&6dógsR^=/fb@s#m} uZh.V80_)$.1W
hS*zQJÑ|ă{nIPa±a#نL<SA
%^yg2*\fxJhQh_FBK(c%cBKwaHeRB 8w6<ϾK @.k*[k|^_¹BV;,pu]24Y
BwԢCm3`>5#FzFG-%Ũ
W0A{TȪ#u4@e24߈*:*6Ђt&XGe@dc%cເh|y$HhGv3s$(Y)sYMvE@lC(.tkب6K(E;Op1?:
D6wОƘfO&zqZ3Z>0MC{ڟi#.
tPڻu-u-t38X Wt2h!.>9;TVKrj_$yABZȊ6.ƭI\yK:¬
s#lhsxzb=INse/FUad4H3lnHo0T^」j*]yfrMY!-#I(YVaΡ@1kE뗴2=qRtۈh@y@(GX)I-Z$lNX,vg^~cE
/虯&jz=АUdY__\FGA} …網站
首先想到編碼問題,參考了《也談Python的中文編碼處理》一文 ,感受基本明白怎麼回事兒了,按理說ui
1
|
isinstance
(html,
str
)
=
=
True
|
而且頁面的編碼肯定爲 GBK,那麼編碼
1
|
html.decode(
'gbk'
).encode(
'utf-8'
)
|
就能夠將機器碼以 gbk 解碼,再從新以 utf-8 編碼,就能夠獲得正確的文本。但是收到這樣的提示:url
UnicodeDecodeError: ‘gbk’ codec can’t decode bytes in position 1-2: illegal multibyte sequencespa
通過在 v2ex 求助,以及反覆折騰了一下發現獲得的果真是 gzip 過的亂碼,因而嘗試經過 zlib 解壓縮code
1
2
|
import
zlib
html
=
zlib.decompress(html)
|
但是卻獲得下面的錯誤
zlib.error: Error -3 while decompressing data: incorrect header check
無奈,只得用 gzip 庫和 StringIO 庫繞路解決
1
2
3
|
import
gzip, StringIO
html
=
gzip.GzipFile(fileobj
=
StringIO.StringIO(html), mode
=
"r"
)
html
=
html.read().decode(
'gbk'
).encode('utf
-
8
’)
|
終於獲得了正確的內容和正確的編碼~ ^^
問題到這裏就解決了,但是對於不能直接使用簡潔的 zlib 庫表示很不甘心,畢竟根據 python 的文檔 gzip 庫也是調用 zlib 來解壓的,爲何不直接用 zlib 呢?功夫不負有心人,最後終於在 StackOverflow 上找到了答案。因而最終代碼以下:
1
2
3
4
5
6
7
8
|
request
=
urllib2.Request(url)
request.add_header(
'Accept-encoding'
,
'gzip'
)
opener
=
urllib2.build_opener()
response
=
opener.
open
(request)html
=
response.read()
gzipped
=
response.headers.get(
'Content-Encoding'
)
if
gzipped:
html
=
zlib.decompress(html,
16
+
zlib.MAX_WBITS)
print
html
|
代碼裏在 request header 中默認加入了接受 gzip,服務器會優先返回 gzip 後的頁面,這樣極大減小數據流的大小,絕大多數服務器都是支持 gzip 的。以後對於意外狀況,也加入了對 response header 的判斷,對於不包含「Content-Encoding」的數據流就不會去對其解壓縮。這樣看上去妥妥的了,但其實仍是會有不少意外情況,超出這篇的範圍,這裏就不涉及了。
後記,後來才知道這是一個很常見的坑,出於對防止抓取的考慮,部分網站採起了各類措施。例如:對於沒有指定 Accept-Encoding 的請求也會返回 gzip 過的內容;會驗證 Request Header 的 User-Agent 和 Referer 甚至 cookies 之類的。對於抓取感興趣的能夠繼續閱讀《用Python抓取網頁的注意事項》,網頁抓取雖然是個很成熟的領域,但門外漢面臨諸多未知的挑戰,惟有多讀多作多積累纔好。