Python urllib.request и вопрос об декодировании utf8

Я пишу простой скрипт CGI Python, который захватывает веб-страницу и отображает 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) 

Этот скрипт отлично работает при запуске из командной строки, но когда он просматривает его с помощью веб-браузера, он показывает пустую страницу. Вот ошибка, которую я получаю в 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) 

  • Python .split () без 'u
  • 3 Solutions collect form web for “Python urllib.request и вопрос об декодировании utf8”

    Когда вы печатаете его в командной строке, вы печатаете строку 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.

    Возможно, сайт, который вы пытаетесь открыть, не кодируется UTF-8. Попробуйте передать "iso-8859-1" методу декодирования.

    Вместо того, чтобы бороться с внутренними sys.stdout , гораздо более прямолинейно, чтобы веб-сервер (1) установил переменную окружения 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 , если вы хотите сохранить структуру такой же, как со всеми другими загрузчиками модулей и конфигурациями.

    Содержимое созданного env_mod.conf файла 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

    Python - лучший язык программирования в мире.