Aktualizacja 2018:
Od lutego 2018 r. Stosowanie kompresji takich jak gzip
stało się dość popularne (korzysta z niej około 73% wszystkich witryn, w tym duże witryny, takie jak Google, YouTube, Yahoo, Wikipedia, Reddit, Stack Overflow i Stack Exchange Network).
Jeśli wykonasz proste dekodowanie, jak w oryginalnej odpowiedzi, z odpowiedzią spakowaną gzipem, pojawi się błąd podobny do tego:
UnicodeDecodeError: kodek „utf8” nie może zdekodować bajtu 0x8b na pozycji 1: nieoczekiwany bajt kodu
Aby zdekodować odpowiedź z gzpipem, musisz dodać następujące moduły (w Pythonie 3):
import gzip
import io
Uwaga: w Pythonie 2 używałbyś StringIO
zamiastio
Następnie możesz przeanalizować zawartość w następujący sposób:
response = urlopen("https://example.com/gzipped-ressource")
buffer = io.BytesIO(response.read()) # Use StringIO.StringIO(response.read()) in Python 2
gzipped_file = gzip.GzipFile(fileobj=buffer)
decoded = gzipped_file.read()
content = decoded.decode("utf-8") # Replace utf-8 with the source encoding of your requested resource
Ten kod odczytuje odpowiedź i umieszcza bajty w buforze. gzip
Moduł odczytuje się bufor za pomocą GZipFile
funkcji. Następnie spakowany plik gzip można ponownie wczytać do bajtów i na końcu zdekodować do normalnie czytelnego tekstu.
Oryginalna odpowiedź z 2010 r .:
Czy możemy uzyskać rzeczywistą wartość używaną do link
?
Ponadto zwykle napotykamy ten problem tutaj, gdy próbujemy .encode()
już zakodowany ciąg bajtów. Więc możesz spróbować najpierw go zdekodować, jak w
html = urllib.urlopen(link).read()
unicode_str = html.decode(<source encoding>)
encoded_str = unicode_str.encode("utf8")
Jako przykład:
html = '\xa0'
encoded_str = html.encode("utf8")
Niepowodzenie
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 0: ordinal not in range(128)
Podczas:
html = '\xa0'
decoded_str = html.decode("windows-1252")
encoded_str = decoded_str.encode("utf8")
Sukces bez błędów. Zwróć uwagę, że użyłem przykładu „windows-1252” . Dostałem to od Chardeta i miałem 0,5 pewności, że to prawda! (cóż, jak w przypadku łańcucha o długości 1 znaku, czego oczekujesz) Powinieneś zmienić to na kodowanie zwracanego ciągu bajtów z .urlopen().read()
na to, co dotyczy pobranej zawartości.
Innym problemem, jaki widzę, jest to, że .encode()
metoda string zwraca zmodyfikowany ciąg i nie modyfikuje źródła w miejscu. Więc jest to trochę bezużyteczne, self.response.out.write(html)
ponieważ html nie jest zakodowanym ciągiem z html.encode (jeśli to jest to, do czego pierwotnie dążyłeś).
Jak zasugerował Ignacio, sprawdź na źródłowej stronie internetowej rzeczywiste kodowanie zwróconego ciągu znaków z read()
. Znajduje się w jednym z metatagów lub w nagłówku ContentType odpowiedzi. Użyj tego jako parametru dla .decode()
.
Należy jednak pamiętać, że nie należy zakładać, że inni programiści są wystarczająco odpowiedzialni, aby upewnić się, że deklaracje nagłówka i / lub zestawu metaznaków odpowiadają rzeczywistej zawartości. (Co jest PITA, tak, należy wiedzieć, że był jednym z tych wcześniej).