2011-01-05 27 views
2

私はWebページを取得し、Webブラウザ(プロキシのように動作する)にHTMLファイルを表示する単純なPython CGIスクリプトを作成しています。ここではスクリプトは次のとおりです。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ではないのですか?)にエンコードし、ヘッダーにもそのように記述します。

したがって、このような何か:Pythonの2の下

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')) 

、これは同様に動作します:

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

しかし、バイト単位でのPython 3の下でエンコードされたデータ、それがうまく印刷されませんので、 。

もちろん、UTF8から最初にデコードしてから再度エンコードすることに気付くでしょう。あなたは厳密に言えば、そうする必要はありません。しかし、その間でHTMLを変更したい場合は、そうすることをお勧めします。すべての変更をUnicodeに入れておきます。

+0

がこの試み。とりわけ、initalのタグの前に "b'00004000 \ r \ n"と表示されます。それをしなければならないのでしょうか?私が間違っていなければ、それは単にバイトコードを意味するのでしょうか? –

+0

@Corey Farwell:ああ、あなたはPython 3を使用していますが、私はそれに気づいていませんでした。私の悪い。ええ、あなたはそれを印刷することはできません、あなたはstdoutにそれを書く必要があります。更新されます。 –

+0

sys.stdout.buffer.write()はStringsを好きではないので、まずContent-typeをutf8にエンコードしてから両方を書き込む必要があります。 「00004000」と最後の行に「00000000」があるWebページの数行(最初の行を含む)を除いてほとんどすべてが機能します。これについてもっと良い方法はありますか?私はstdoutを使うのがちょうどハックだと感じています。 wsgiはこれをより簡単にしますか? –

1

開こうとしているサイトがUTF-8でエンコードされていない可能性があります。 "iso-8859-1"をデコードメソッドに渡してみてください。

+0

いいえ、それは* encode *エラーではなく、* decode *エラーを返します。 –

0

sys.stdoutの内部で作業するのではなく、WebサーバーでCGI環境変数PYTHONIOENCODING(2)をUTF8に設定するのがより簡単です。

Apache2の場合は、mod_env.soの読み込みを有効にする必要があります。 Debianのインストールでは、/etc/apache2/mods-enabledから/etc/apache2/mods-available/env.loadにシンボリックリンクを作成し、コンフィギュレーション/etc/apache2/conf-available/env.confを作成し、/etc/apache2/conf-enabledのシンボリックリンクを他のすべてのモジュールローダとconfigsと同じにしたい場合は、このリンクを使用します。

私が作成したenv_mod.confファイルの内容は次のとおりです。私はこれをしなかった前

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

、私のスクリプトはsys.stdout.encodingが、それは、その後、"ANSI ..."およびUnicode文字を含む文字列を印刷しようとしたときに出てerroringたことを報告しました。 "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