我正在编写一个简单的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)
答案 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。