2011-01-05 6 views
2

웹 페이지를 잡고 프록시처럼 작동하는 웹 브라우저에 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) 

이 스크립트는 잘 작동하지만 웹 브라우저를 볼 수에 도달하면 빈 페이지를 보여줍니다 여기에 스크립트입니다. 다음은 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

명령 줄에서 인쇄 할 때 유니 코드 문자열이 터미널에 인쇄됩니다. 터미널에는 인코딩이 있으므로 파이썬은 유니 코드 문자열을 해당 인코딩으로 인코딩합니다. 이것은 잘 작동합니다.

CGI에서 사용하면 인코딩이없는 stdout으로 인쇄됩니다. 따라서 파이썬은 문자열을 ASCII로 인코딩하려고합니다. 이것은 ASCII가 인쇄하려고하는 모든 문자를 포함하지 않으므로 실패하므로 위의 오류가 발생합니다.

이 문제를 해결하려면 문자열을 일종의 인코딩 (UTF8이 아닌 이유)으로 인코딩하고 헤더에서도 그렇게 말하십시오.

그래서 이런 식으로 뭔가 : 그것은 잘 인쇄되지 않도록, 바이트 단위로 인코딩 된 데이터를

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

을하지만 파이썬 3 미만 :

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

에서 파이썬 2, 이것은 잘 작동 .

물론 UTF8에서 먼저 디코딩 한 다음 다시 인코딩해야합니다. 엄밀히 말하면, 그렇게 할 필요는 없습니다. 그러나 HTML을 중간에서 수정하려면 실제로 그렇게하고 유니 코드에서 모든 수정 사항을 유지하는 것이 좋습니다.

+0

는이 시도.무엇보다도 인위적인 태그 앞에 "b'00004000 \ r \ n"이 인쇄됩니다. 그 일을해야합니까? 내가 잘못 본 것이 아니라면, 그것이 바이트 코드라는 것을 의미합니까? –

+0

@Corey Farwell : 아, 파이썬 3을 사용하고 있습니다. 내 잘못이야. 예, 인쇄 할 수 없으면 표준 출력에 써야합니다. 업데이트됩니다. –

+0

sys.stdout.buffer.write()는 Strings을 좋아하지 않으므로 먼저 Content-type을 utf8로 인코딩 한 다음 두 가지를 모두 써야합니다. 거의 모든 것은 '00004000'이 있고 마지막 줄은 '00000000'인 웹 페이지의 몇 줄 (맨 처음 줄 포함)을 제외하고 작동합니다. 이 문제를 해결할 더 좋은 방법이 없을까요? stdout을 사용하는 것만으로도 해킹 된 것 같은 기분이 듭니다. wsgi로이 작업을보다 쉽게 ​​할 수 있습니까? –

1

열려는 사이트가 UTF-8로 인코딩되지 않았을 수 있습니다. "iso-8859-1"을 디코드 메소드에 전달하십시오.

+0

아니요. * encode * 오류가 아니라 * 디코드 * 오류를 줄 것입니다. –

0

sys.stdout 내부와 씨름하는 것이 아니라 웹 서버에서 CGI 환경 변수 PYTHONIOENCODING (2)을 UTF8으로 설정하는 것이 훨씬 간단합니다.

Apache2의 경우로드를 활성화해야 mod_env.so이됩니다. 데비안 설치에서는 심볼릭 링크를 /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 ..." 것을보고 된 유니 코드 문자를 포함하는 문자열을 인쇄 할 때 밖으로 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