Cập nhật 2018:
Kể từ tháng 2 năm 2018, việc sử dụng nén như gzip
đã trở nên khá phổ biến (khoảng 73% tất cả các trang web sử dụng nó, bao gồm các trang web lớn như Google, YouTube, Yahoo, Wikipedia, Reddit, Stack Overflow và Stack Exchange Network).
Nếu bạn thực hiện một giải mã đơn giản như trong câu trả lời ban đầu với phản hồi được nén, bạn sẽ gặp một lỗi giống hoặc tương tự như sau:
UnicodeDecodeError: 'utf8' codec không thể giải mã byte 0x8b ở vị trí 1: byte mã không mong muốn
Để giải mã một phản hồi gzpipped, bạn cần thêm các mô-đun sau (trong Python 3):
import gzip
import io
Lưu ý: Trong Python 2 bạn sử dụng StringIO
thay vìio
Sau đó, bạn có thể phân tích nội dung như thế này:
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
Mã này đọc phản hồi và đặt các byte vào bộ đệm. Các gzip
mô-đun sau đó đọc bộ đệm bằng cách sử dụng GZipFile
chức năng. Sau đó, tệp gzipped có thể được đọc lại thành byte và được giải mã thành văn bản thường có thể đọc được.
Câu trả lời gốc từ năm 2010:
Chúng ta có thể nhận được giá trị thực tế được sử dụng cho link
?
Ngoài ra, chúng ta thường gặp phải vấn đề này ở đây khi chúng ta đang cố gắng để .encode()
một chuỗi byte đã được mã hóa. Vì vậy, bạn có thể cố gắng giải mã nó đầu tiên như trong
html = urllib.urlopen(link).read()
unicode_str = html.decode(<source encoding>)
encoded_str = unicode_str.encode("utf8")
Ví dụ:
html = '\xa0'
encoded_str = html.encode("utf8")
Thất bại với
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 0: ordinal not in range(128)
Trong khi:
html = '\xa0'
decoded_str = html.decode("windows-1252")
encoded_str = decoded_str.encode("utf8")
Thành công không có lỗi. Xin lưu ý rằng "windows-1252" là thứ tôi sử dụng làm ví dụ . Tôi đã nhận được điều này từ chardet và nó đã tự tin 0,5 rằng nó đúng! (tốt, như được đưa ra với chuỗi có độ dài 1 ký tự, bạn mong đợi điều gì) Bạn nên thay đổi điều đó thành mã hóa chuỗi byte được trả về từ .urlopen().read()
nội dung bạn áp dụng cho nội dung bạn đã truy xuất.
Một vấn đề khác tôi thấy đó là .encode()
phương thức chuỗi trả về chuỗi đã sửa đổi và không sửa đổi nguồn tại chỗ. Vì vậy, thật vô dụng khi có self.response.out.write(html)
html không phải là chuỗi được mã hóa từ html.encode (nếu đó là những gì bạn đã nhắm đến ban đầu).
Như Ignacio đã đề xuất, hãy kiểm tra trang web nguồn để biết mã hóa thực tế của chuỗi được trả về từ đó read()
. Đó là một trong các thẻ Meta hoặc trong tiêu đề ContentType trong phản hồi. Sử dụng sau đó làm tham số cho .decode()
.
Tuy nhiên, xin lưu ý rằng không nên giả định rằng các nhà phát triển khác chịu trách nhiệm đủ để đảm bảo các khai báo bộ tiêu đề và / hoặc ký tự meta khớp với nội dung thực tế. (Đó là một PITA, vâng, tôi nên biết, tôi là một trong số đó trước đây).