Python urllib.request和utf8解码问题

Cor*_*ell 2 python unicode cgi apache2 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)
Run Code Online (Sandbox Code Playgroud)

从命令行运行时,此脚本可以正常工作,但是当它通过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)
Run Code Online (Sandbox Code Playgroud)

Len*_*bro 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'))
Run Code Online (Sandbox Code Playgroud)

在Python 2下,这也可以:

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

但是在Python 3下,编码数据以字节为单位,因此打印效果不佳.

当然你会注意到你现在首先从UTF8解码,然后重新编码.严格来说,你不需要这样做.但是如果你想在两者之间修改HTML,实际上这可能是一个好主意,并保留所有的Unicode修改.