إذا قام خادم http بإرجاع نوع المحتوى: text / * بدون ترميز ، فإن Response.text تقوم دائمًا بفك ترميزها على أنها نص "ISO-8859-1".
قد يكون صالحًا في RFC2616 / 3.7.1 ، لكن هذا خطأ في الحياة الواقعية في عام 2013.
لقد صنعت نموذجًا لصفحة بنص صيني:
http://lavr.github.io/python-emails/tests/requests/some-utf8-text.html
جميع المتصفحات تجعل هذه الصفحة بشكل صحيح.
لكن reguests.get يقوم بإرجاع نص غير صالح.
وإليك اختبار بسيط باستخدام عنوان url هذا:
https://gist.github.com/lavr/6572927
شكرا لهذا lavr!
هذا قرار تصميم متعمد للطلبات. نحن نتبع المواصفات ما لم نجد أنفسنا في وضع تختلف فيه المواصفات بشكل كبير عن سلوك العالم الحقيقي بحيث تصبح مشكلة (على سبيل المثال ، احصل على استجابة بعد 302 من POST).
إذا كان الخادم الرئيسي يعرف ما هو الترميز الصحيح ، فيجب أن يشير إليه. خلاف ذلك ، سوف نتبع ما تقوله المواصفات. =)
إذا كنت تعتقد أن المواصفات الافتراضية سيئة ، فأنا أشجعك بشدة على المشاركة في عملية RFC لـ HTTP / 2.0 من أجل تغيير هذا الإعداد الافتراضي. =)
ما قالهLukasa + حقيقة أنه إذا كان الترميز الذي تم استرداده من الرؤوس غير موجود ، فإننا نعتمد على التمثيلية للتخمين عند الترميز. مع عدد قليل جدًا من الأحرف ، لن تُرجع التمثيلية أي شيء نهائي لأنها تستخدم بيانات إحصائية لتخمين الترميز الصحيح.
بصراحة ، العام لا يحدث فرقا ولا يغير المواصفات أيضا.
إذا كنت تعرف الترميز الذي تتوقعه ، فيمكنك أيضًا القيام بفك التشفير بنفسك كما يلي:
text = str(r.content, '<ENCODING>', errors='replace')
لا حرج في الطلبات بقدر ما أشعر بالقلق وهذا ليس خطأ في التمثيلية أيضًا. نظرًا لأن Lukasa يبدو أنه يتفق معي ، فأنا أغلق هذا.
lavr (/ cc @ sigmavirus24) ، أسهل من ذلك ، يمكنك ببساطة توفير التشفير بنفسك.
>>> r = requests.get('http://irresponsible-server/')
>>> r.encoding = 'utf-8'
ثم تابع بشكل طبيعي.
@ kennethreitz هذا مخيب للآمال. لماذا نجعل ذلك سهلاً على الناس؟ = ص
على الاطلاق :)
في الغالب للمواقع اليابانية. كلهم يكذبون بشأن ترميزهم.
@ sigmavirus24
يرجى ملاحظة أن uses.get_encoding_from_headers تُرجع دائمًا "ISO-8859-1" ، وليس هناك فرصة لاستدعاء التمثيلية.
الخطأ هو: نتوقع أن يتم استخدام التمثيلية لتخمين الترميز ، لكنها ليست كذلك.
يعمل التصحيح أعلاه على إصلاح الخلل ، ولكنه لا يزال يتبع RFC.
من فضلك ، فكر في مراجعته.
lavr معذرة ، لم نوضح ذلك تمامًا. لا نتوقع أن يتم استدعاء التمثيلية في هذه الحالة. إن RFC واضح للغاية: إذا لم تحدد مجموعة أحرف ، وكان نوع MIME text/*
، فيجب افتراض أن الترميز هو ISO-8859-1. هذا يعني "لا تخمن". =)
lavr : فقط r.encoding
إلى None
، وسيعمل كما تتوقع (على ما أظن).
أو افعل r.encoding = r.apparent_encoding
.
حتى أفضل.
في r.encoding = None
و r.encoding = r.apparent_encoding
فقدنا معلومات مجموعة أحرف الخادم.
أعتقد أن تجاهل رأس الخادم تمامًا ليس حلاً جيدًا.
الحل الصحيح هو شيء من هذا القبيل:
r = requests.get(...)
params = cgi.parse_header(r.headers.get('content-type'))[0]
server_encoding = ('charset' in params) and params['charset'].strip("'\"") or None
r.encoding = server_encoding or r.apparent_encoding
text = r.text
تبدو غريبة :(
أو افعل هذا:
r = requests.get(...)
if r.encoding is None or r.encoding == 'ISO-8859-1':
r.encoding = r.apparent_encoding
لا أعتقد ذلك :)
الشرط r.encoding is None
لا معنى له ، لأن r.encoding لا يمكن أبدًا أن يكون بلا لنوع المحتوى = text / *.
r.encoding == 'ISO-8859-1'
... ماذا يعني ذلك؟ أرسل الخادم charset = 'ISO-8859-1' أم أن الخادم لم يرسل مجموعة أحرف؟ If first، I shouldn't guess charset. إذا أولاً ، لا يجب أن أخمن الأحرف.
lavr كنت أغطي القواعد غير النصية. يمكنك استبعاد احتمال charset
باستخدام هذا الشرط بدلاً من ذلك:
r.encoding == 'ISO-8859-1' and not 'ISO-8859-1' in r.headers.get('Content-Type', '')
تضمين التغريدة
حسنًا ، يمكنني استخدام هذا الاختراق.
ويمكن للجميع في أوروبا الشرقية وآسيا استخدامه.
ولكن ماذا لو أصلحناه في الطلبات؟ ؛)
ماذا لو تمكنت الطلبات من تعيين enconding=None
بأمانة عند الاستجابة بدون مجموعة أحرف؟
كما ناقشنا عدة مرات ، تتبع الطلبات مواصفات HTTP حرفياً. السلوك الحالي ليس خطأ. =)
حقيقة أنها ليست مفيدة لحالة الاستخدام الخاصة بك هي قصة أخرى كاملة. =)
حسنًا ، هذا نقاش كافٍ حول هذا. شكرا على ملاحظاتك.
تم تحديث HTTP 1.1 مع مجموعة أحرف افتراضية ISO-8859-1: http://tools.ietf.org/html/rfc7231#appendix -B
نحن بالفعل نتتبع هذا في # 2086. =)
لمن يهمه الأمر ، يوجد هنا تصحيح التوافق
قم بإنشاء ملف requests_patch.py
بالكود التالي وقم باستيراده ، ثم يجب حل المشكلة.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import chardet
def monkey_patch():
prop = requests.models.Response.content
def content(self):
_content = prop.fget(self)
if self.encoding == 'ISO-8859-1':
encodings = requests.utils.get_encodings_from_content(_content)
if encodings:
self.encoding = encodings[0]
else:
self.encoding = chardet.detect(_content)['encoding']
if self.encoding:
_content = _content.decode(self.encoding, 'replace').encode('utf8', 'replace')
self._content = _content
self.encoding = 'utf8'
return _content
requests.models.Response.content = property(content)
monkey_patch()
lavr (/ cc @ sigmavirus24) ، أسهل من ذلك ، يمكنك ببساطة توفير التشفير بنفسك.
>>> r = requests.get('http://irresponsible-server/') >>> r.encoding = 'utf-8'
ثم تابع بشكل طبيعي.
شكرا على هذا! أي فكرة عن كيفية القيام بذلك في سطر واحد؟
التعليق الأكثر فائدة
lavr (/ cc @ sigmavirus24) ، أسهل من ذلك ، يمكنك ببساطة توفير التشفير بنفسك.
ثم تابع بشكل طبيعي.