Python urllib.request和utf8解码问题

时间:2011-01-05 08:01:21

标签: python unicode apache2 cgi unicode-string

我正在编写一个简单的Python CGI脚本,用于抓取网页并在Web浏览器中显示HTML文件(充当代理)。这是脚本:

#!/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)

3 个答案:

答案 0 :(得分:5)

在命令行打印时,将Unicode字符串打印到终端。终端具有编码,因此Python会将您的Unicode字符串编码为该编码。这样可以正常工作。

在CGI中使用它时,最终会打印到没有编码的stdout。因此,Python尝试使用ASCII对字符串进行编码。这会失败,因为ASCII不包含您尝试打印的所有字符,因此您会收到上述错误。

对此的修复是将您的字符串编码为某种编码(为什么不编码UTF8?),并在标题中也这样说。

这样的事情:

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下,这也可行:

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修改。

答案 1 :(得分:1)

您尝试打开的网站可能不是UTF-8编码的。尝试将"iso-8859-1"传递给解码方法。

答案 2 :(得分:0)

更直接的是让Web服务器(1)将CGI环境变量sys.stdout(2)设置为PYTHONIOENCODING,而不是与UTF8内部进行搏斗。 / p>

对于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和符号链接,如果您愿意保持结构与所有其他模块加载器和配置相同。

我创建的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