Python requests 中文亂碼解決方法

對python3 而言,亂碼不叫亂碼,叫做編碼錯誤
沒錯,python3 內所有的文字都是 unicode 類型的str,對於原本是utf-8 編碼的文字,再另外編成big5 後當然會出現亂碼

一般而言透過requests 訪問網頁的程式碼如下:

1
2
3
4
5
6
# -*- encoding: utf8-*-
import lxml, requests

result = requests.get('http://disp.cc/');
print("encoding: %s" % result.encoding)
print("content: \n%s" % result.text)

request 取得的網頁會自動判別網頁編碼,但為了保險起見我會自行設定
下方是自動判斷編碼時的輸出:

1
2
3
4
5
6
7
8
9
10
encoding: utf-8
content:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Disp BBS</title>
<meta property="fb:app_id" content="111318672241067"/>
<meta property="og:title" content="網頁型電子佈告欄系統 - Disp BBS"/>
下略 ....

另外我們來看 big5的網站會怎麼編碼,這邊用銘傳大學的課程查詢當範例

1
2
3
4
5
6
# -*- encoding: utf8-*-
import lxml, requests

result = requests.get('http://www.mcu.edu.tw/student/new-query/sel-query/qslist_1.asp');
print("encoding: %s" % result.encoding)
print("content: \n%s" % result.text)

輸出:

1
2
3
4
5
6
7
8
9
10
encoding: ISO-8859-1
content:

<html>
<head>
<title>¯S®í±ø¥ó¬d¸ßµ²ªG</title>
<meta http-equiv="Content-Type" content="text/html; charset=big5">
<link href="cls.css" rel="stylesheet" type="text/css">
</head>
下略...

從輸出可以猜測 requests 的預設編碼可能是:ISO-8859-1,但是在 <meta charset>這邊我們發現網站是用big5 編碼的,所以我們需要手動指定 requests 的編碼方式:

1
2
3
4
result = requests.get('http://www.mcu.edu.tw/student/new-query/sel-query/qslist_1.asp');
result.encoding = 'big5'
print("encoding: %s" % result.encoding)
print("content: \n%s" % result.text)

這樣我們的輸出就會正常了:

1
2
3
4
5
6
7
8
9
10
encoding: big5
content:

<html>
<head>
<title>特殊條件查詢結果</title>
<meta http-equiv="Content-Type" content="text/html; charset=big5">
<link href="cls.css" rel="stylesheet" type="text/css">
</head>
下略 ...

但是把encoding 設成big5 仍然不是一個好的做法,你會遇到某些網站雖然已經轉成big5,但他的輸出仍是亂碼

1
2
3
4
5
6
7
8
# -*- encoding: utf8-*-
import lxml, requests

result = requests.post('http://www.mcu.edu.tw/student/new-query/sel-query/qslist_1.asp',
data={'dept1': '02'});
result.encoding = 'big5'
print("encoding: %s" % result.encoding)
print("content: \n%s" % result.text)

輸出篇幅太長,我直接擷取有問題的輸出

1
2
3
4
5
<td align="center">
<a href="https://tch.mcu.edu.tw/sylwebqry/pro10_22.aspx?tcls=02252&tcour=00221&tyear=&tsem=&teac=&type=1" target="_bank">
<font size="-1" color="#0080FF">正課: 李�X煌</font>
</a>
</td>

原字碼為:正課: 李鞇煌
這是因為 並非big5 的字碼,而是基於big5 而擴展的香港增補字,雖然都是big5,但是big5 有不同的種類,例如微軟cmd 的編碼為: cp950,而基於香港增補字擴充的編碼則為: big5hkscs
可以在這網頁內查到其他相關的說明,在網頁內的 Encodings that can encode this properly欄位內可以查到該字可支援的編碼,僅支援: utf_8 utf_16 gbk gb18030 big5hkscs 這幾種。

既然我們知道不能用big5 編碼了,那我們就要來修改一下程式:

1
2
3
4
5
6
7
8
# -*- encoding: utf8 -*-
import lxml, requests

result = requests.post('http://www.mcu.edu.tw/student/new-query/sel-query/qslist_1.asp',
data={'dept1': '02'});
result.encoding = 'big5-hkscs'
print("encoding: %s" % result.encoding)
print("content: \n%s" % result.text)

輸出就正常了:

1
2
3
4
5
<td align="center">
<a href="https://tch.mcu.edu.tw/sylwebqry/pro10_22.aspx?tcls=02252&tcour=00221&tyear=&tsem=&teac=&type=1" target="_bank">
<font size="-1" color="#0080FF">正課: 李鞇煌</font>
</a>
</td>

結論

網頁編碼有utf8 就用utf8
若為big5 則轉換為 big5-hkscs