2011-01-05 109 views
2

我正在寫一個簡單的Python CGI腳本,它抓取一個網頁並在Web瀏覽器中顯示HTML文件(就像一個代理)。以下是腳本:Python urllib.request和utf8解碼問題

#!/usr/bin/env python3.0 

import urllib.request 

site = "http://reddit.com/" 
site = urllib.request.urlopen(site) 
site = site.read() 
site = site.decode('utf8') 

print("Content-type: text/html\n\n") 
print(site) 

這個腳本在命令行運行時工作正常,但是當它通過Web瀏覽器查看時,它顯示一個空白頁。下面是我在Apache的error_log中出現錯誤:

Traceback (most recent call last): 
    File "/home/public/projects/proxy/script.cgi", line 11, in <module> 
    print(site) 
    File "/usr/local/lib/python3.0/io.py", line 1491, in write 
    b = encoder.encode(s) 
    File "/usr/local/lib/python3.0/encodings/ascii.py", line 22, in encode 
    return codecs.ascii_encode(input, self.errors)[0] 
UnicodeEncodeError: 'ascii' codec can't encode character '\u2019' in position 33777: ordinal not in range(128) 

回答

5

當你在命令行打印,打印一個Unicode字符串到終端。終端有一個編碼,所以Python會將您的Unicode字符串編碼爲該編碼。這將工作正常。

當你在CGI中使用它時,你最終打印到沒有編碼的stdout。因此,Python嘗試使用ASCII對字符串進行編碼。這會失敗,因爲ASCII不包含您嘗試打印的所有字符,所以會出現上述錯誤。

解決方法是將您的字符串編碼爲某種編碼(爲什麼不是UTF8?),並在頭文件中說明。

因此,像這樣:以字節爲單位

print("Content-type: text/html;encoding=UTF-8\n\n") # Not 100% sure about the spelling. 
print(site.encode('UTF8')) 

但是Python的3下的編碼數據,所以它不會打印好:

sys.stdout.buffer.write(b"Content-type: text/html;encoding=UTF-8\n\n") # Not 100% sure about the spelling. 
sys.stdout.buffer.write(site.encode('UTF8')) 

在Python 2中,這將工作以及。

當然,您會注意到您現在首先從UTF8進行解碼,然後對其進行重新編碼。嚴格來說,你不需要那樣做。但是如果你想在兩者之間修改HTML,那麼這樣做可能是一個好主意,並且保留Unicode中的所有修改。

+0

嘗試這樣做。除此之外,它在inital 標籤前打印:「b'00004000 \ r \ n」。它應該這樣做嗎?如果我沒有弄錯,那只是說它是字節碼? – 2011-01-05 08:16:35

+0

@Corey Farwell:哦,你使用的是Python 3,我沒有注意到。我的錯。是的,那麼你不能打印它,你必須寫它到標準輸出。將更新。 – 2011-01-05 08:19:22

+0

sys.stdout.buffer.write()不喜歡字符串,因此您必須先將Content-type編碼爲utf8,然後再編寫兩者。除了幾行(包括第一行)具有'00004000'且最後一行具有'00000000'之外,幾乎所有東西都可以工作。有沒有更好的方法去做這件事?我覺得使用stdout只是一個破解。 wsgi會讓這更容易嗎? – 2011-01-05 08:30:34

1

可能是您嘗試打開的網站不是UTF-8編碼的。嘗試將"iso-8859-1"傳遞給解碼方法。

+0

不,這會給他*解碼*錯誤,而不是*編碼*錯誤。 – 2011-01-05 08:15:27

0

而不是摔跤與sys.stdout內部,更直接的是讓Web服務器(1)將CGI環境變量PYTHONIOENCODING(2)設置爲UTF8

對於Apache2,您必須啓用加載mod_env.so。在Debian安裝,這相當於創造了/etc/apache2/mods-enabled一個符號鏈接/etc/apache2/mods-available/env.load,並在/etc/apache2/conf-enabled創建配置/etc/apache2/conf-available/env.conf,以及一個符號鏈接,如果你想保留的結構相同的結構與所有其它模塊加載和CONFIGS。

env_mod.conf文件我創建的內容是:

<IfModule mod_env.c> 
    SetEnv PYTHONIOENCODING UTF8 
</IfModule> 

我這樣做之前,我的劇本報告說sys.stdout.encoding"ANSI ...",並試圖打印包含Unicode字符的字符串時,示數出來,後來,它是"UTF8",並正確地將所需的UTF-8發送到瀏覽器。

(1)http://httpd.apache.org/docs/2.2/howto/cgi.html#env

(2)http://docs.python.org/3.3/library/sys.html#sys.stdin